From patchwork Mon Mar 30 11:24:39 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Baryshkov X-Patchwork-Id: 6120311 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id CDCEFBF4A6 for ; Mon, 30 Mar 2015 11:27:32 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BCE3E20374 for ; Mon, 30 Mar 2015 11:27:31 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id A120020373 for ; Mon, 30 Mar 2015 11:27:30 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1YcXoi-00077h-NT; Mon, 30 Mar 2015 11:25:16 +0000 Received: from mail-pd0-x22d.google.com ([2607:f8b0:400e:c02::22d]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1YcXod-0005tS-HY for linux-arm-kernel@lists.infradead.org; Mon, 30 Mar 2015 11:25:13 +0000 Received: by pdnc3 with SMTP id c3so173012441pdn.0 for ; Mon, 30 Mar 2015 04:24:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id; bh=XGwyBDMLTjo+js6ydbSi+pxK+5Y+eT/g9/UgJjtWsrk=; b=jJpVO/8WC/pXAmsqzv6CrrfCGdJhrUjcO9K7wWN+Y36a2P9J3Nrg87e72MHctHW9YQ Wk/W0PzpGW1sY2zQm2cdN+bITltbkfbxl9OGtVsO+HROtRN9i/4CpBxrs4uGBRZgoquS D0RTt3j/Ie/aZmxXMD87YLosngLW/KlSBvAf1I53PP9dL7wzuiaDPafiYfmS1AHG8r2n kXtMXG4kiqbk0b7fh/oFT1tVadBhF9oINqWhw8RjdTw940NFliaG0DpvZgW6tuDf06nG y0VeCHO70xTHuGyXj0oFqkIDBUssyPoRTWKs4yaW6urh6iq7E8yDkwU8l3k1FeVj1AEx 0KOg== X-Received: by 10.68.237.198 with SMTP id ve6mr58488517pbc.115.1427714690454; Mon, 30 Mar 2015 04:24:50 -0700 (PDT) Received: from fangorn.rup.mentorg.com (nat-min.mentorg.com. [139.181.32.34]) by mx.google.com with ESMTPSA id sf6sm10307501pbb.82.2015.03.30.04.24.48 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 30 Mar 2015 04:24:49 -0700 (PDT) From: Dmitry Eremin-Solenikov To: Sebastian Reichel Subject: [PATCH] power: add poodle battery driver Date: Mon, 30 Mar 2015 14:24:39 +0300 Message-Id: <1427714679-27472-1-git-send-email-dbaryshkov@gmail.com> X-Mailer: git-send-email 2.1.4 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150330_042511_734281_5D439322 X-CRM114-Status: GOOD ( 17.84 ) X-Spam-Score: -0.8 (/) Cc: Andrea Adami , linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Add a driver supporting battery charging on Sharp SL-5600 (poodle). Voltage and temperature readings are provided through add7846 hwmon interface. Battery voltage is in1_input (mV) and temp in in0_input (values unknown, but should be less than 2441). Signed-off-by: Dmitry Eremin-Solenikov --- drivers/power/Kconfig | 7 ++ drivers/power/Makefile | 1 + drivers/power/poodle_battery.c | 250 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 258 insertions(+) create mode 100644 drivers/power/poodle_battery.c diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 27b751b..678f23e 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -137,6 +137,13 @@ config BATTERY_COLLIE Say Y to enable support for the battery on the Sharp Zaurus SL-5500 (collie) models. +config BATTERY_POODLE + tristate "Sharp SL-5600 (poodle) battery" + depends on MACH_POODLE + help + Say Y to enable support for the battery on the Sharp Zaurus + SL-5600 (poodle) models. + config BATTERY_IPAQ_MICRO tristate "iPAQ Atmel Micro ASIC battery driver" depends on MFD_IPAQ_MICRO diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 36f9e0d..ff3e793 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o +obj-$(CONFIG_BATTERY_POODLE) += poodle_battery.o obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o diff --git a/drivers/power/poodle_battery.c b/drivers/power/poodle_battery.c new file mode 100644 index 0000000..ad96e13 --- /dev/null +++ b/drivers/power/poodle_battery.c @@ -0,0 +1,250 @@ +/* + * Battery handler for Sharp SL-5600 Poodle device + * + * Copyright (c) 2014 Dmitry Eremin-Solenikov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include + +#include + +struct poodle_bat { + int status; + struct power_supply psy; + + struct mutex work_lock; /* protects data */ + + bool (*is_present)(struct poodle_bat *bat); + int technology; +}; + +static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ +static struct work_struct bat_work; + +static struct poodle_bat poodle_bat_main; + +static int poodle_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct poodle_bat *bat = container_of(psy, struct poodle_bat, psy); + + if (bat->is_present && !bat->is_present(bat) + && psp != POWER_SUPPLY_PROP_PRESENT) { + return -ENODEV; + } + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = bat->status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = bat->technology; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bat->is_present ? bat->is_present(bat) : 1; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static bool poodle_bat_is_present(struct poodle_bat *bat) +{ + return gpio_get_value(POODLE_GPIO_BAT_COVER); +} + +static void poodle_bat_external_power_changed(struct power_supply *psy) +{ + schedule_work(&bat_work); +} + +static irqreturn_t poodle_bat_gpio_isr(int irq, void *data) +{ + pr_info("poodle_bat_gpio irq\n"); + schedule_work(&bat_work); + return IRQ_HANDLED; +} + +static void poodle_bat_update(struct poodle_bat *bat) +{ + int old; + struct power_supply *psy = &bat->psy; + + mutex_lock(&bat->work_lock); + + old = bat->status; + + if (bat->is_present && !bat->is_present(bat)) { + pr_info("%s not present\n", psy->name); + bat->status = POWER_SUPPLY_STATUS_UNKNOWN; + gpio_set_value(POODLE_GPIO_JK_B, 0); + gpio_set_value(POODLE_GPIO_CHRG_ON, 0); + } else if (power_supply_am_i_supplied(psy)) { + gpio_set_value(POODLE_GPIO_JK_B, 1); + if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) { + gpio_set_value(POODLE_GPIO_CHRG_ON, 1); + msleep(20); + } + + if (gpio_get_value(POODLE_GPIO_CHRG_FULL)) { + gpio_set_value(POODLE_GPIO_CHRG_ON, 0); + bat->status = POWER_SUPPLY_STATUS_FULL; + } else { + gpio_set_value(POODLE_GPIO_CHRG_ON, 1); + bat->status = POWER_SUPPLY_STATUS_CHARGING; + } + } else { + gpio_set_value(POODLE_GPIO_JK_B, 0); + gpio_set_value(POODLE_GPIO_CHRG_ON, 0); + bat->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + if (old != bat->status) + power_supply_changed(psy); + + mutex_unlock(&bat->work_lock); +} + +static void poodle_bat_work(struct work_struct *work) +{ + poodle_bat_update(&poodle_bat_main); +} + +static enum power_supply_property poodle_bat_main_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_PRESENT, +}; + +static struct poodle_bat poodle_bat_main = { + .status = POWER_SUPPLY_STATUS_DISCHARGING, + .psy = { + .name = "main-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = poodle_bat_main_props, + .num_properties = ARRAY_SIZE(poodle_bat_main_props), + .get_property = poodle_bat_get_property, + .external_power_changed = poodle_bat_external_power_changed, + .use_for_apm = 1, + }, + + .is_present = poodle_bat_is_present, + + .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, +}; + +static struct gpio poodle_batt_gpios[] = { + { POODLE_GPIO_BAT_COVER, GPIOF_IN, "main battery cover" }, + { POODLE_GPIO_CHRG_FULL, GPIOF_IN, "main battery full" }, + { POODLE_GPIO_JK_B, GPIOF_OUT_INIT_LOW, "main charge on" }, + { POODLE_GPIO_CHRG_ON, GPIOF_OUT_INIT_LOW, "main charge on" }, + { POODLE_GPIO_BYPASS_ON, GPIOF_OUT_INIT_LOW, "main charge bypass" }, + /* on for now */ + { POODLE_GPIO_ADC_TEMP_ON, GPIOF_OUT_INIT_HIGH, "main battery temp" }, +}; + +#ifdef CONFIG_PM +static int poodle_battery_suspend(struct device *dev) +{ + /* flush all pending status updates */ + flush_work(&bat_work); + return 0; +} + +static int poodle_battery_resume(struct device *dev) +{ + /* things may have changed while we were away */ + schedule_work(&bat_work); + return 0; +} + +static SIMPLE_DEV_PM_OPS(poodle_battery_pm, + poodle_battery_suspend, + poodle_battery_resume); + +#define POODLE_BATTERY_PM (&poodle_battery_pm) + +#else +#define POODLE_BATTERY_PM NULL +#endif + +static int poodle_battery_probe(struct platform_device *dev) +{ + int ret; + + ret = gpio_request_array(poodle_batt_gpios, + ARRAY_SIZE(poodle_batt_gpios)); + if (ret) + return ret; + + mutex_init(&poodle_bat_main.work_lock); + + INIT_WORK(&bat_work, poodle_bat_work); + + ret = power_supply_register(&dev->dev, &poodle_bat_main.psy); + if (ret) + goto err_psy_reg_main; + + ret = devm_request_irq(&dev->dev, gpio_to_irq(POODLE_GPIO_CHRG_FULL), + poodle_bat_gpio_isr, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "main full", NULL); + if (ret) + goto err_psy_irq; + + ret = devm_request_irq(&dev->dev, gpio_to_irq(POODLE_GPIO_BAT_COVER), + poodle_bat_gpio_isr, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "battery cover", NULL); + if (ret) + goto err_psy_irq; + + return 0; + +err_psy_irq: + power_supply_unregister(&poodle_bat_main.psy); + + cancel_work_sync(&bat_work); +err_psy_reg_main: + gpio_free_array(poodle_batt_gpios, ARRAY_SIZE(poodle_batt_gpios)); + + return ret; +} + +static int poodle_battery_remove(struct platform_device *dev) +{ + power_supply_unregister(&poodle_bat_main.psy); + + cancel_work_sync(&bat_work); + + gpio_free_array(poodle_batt_gpios, ARRAY_SIZE(poodle_batt_gpios)); + + return 0; +} + +static struct platform_driver poodle_battery_driver = { + .driver.name = "poodle-battery", + .probe = poodle_battery_probe, + .remove = poodle_battery_remove, + .driver.pm = POODLE_BATTERY_PM, +}; + +module_platform_driver(poodle_battery_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Dmitry Eremin-Solenikov"); +MODULE_DESCRIPTION("Poodle battery driver"); +MODULE_ALIAS("platform:poodle-battery");