diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 3dfa68eb9af..4487361e55c 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -219,6 +219,12 @@ config CHARGER_MAX8903 pins based on the status of charger connections with interrupt handlers. +config CHARGER_MAX8903B + tristate "MAXIM 8903B Charger (HP/Palm)" + default n + help + Say Y to enable support for the MAX8903B charger. + config CHARGER_TWL4030 tristate "OMAP TWL4030 BCI charger driver" depends on TWL4030_CORE @@ -352,4 +358,6 @@ config PM8921_BMS help Say Y here to enable support for pm8921 chip bms subdevice + + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 03db83973c0..e72d7b00a99 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o obj-$(CONFIG_BATTERY_INTEL_MID) += intel_mid_battery.o obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o +obj-$(CONFIG_CHARGER_MAX8903B) += max8903b_charger.o obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_BATTERY_MSM) += msm_battery.o diff --git a/drivers/power/max8903b_charger.c b/drivers/power/max8903b_charger.c new file mode 100644 index 00000000000..e5d9b70310a --- /dev/null +++ b/drivers/power/max8903b_charger.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2010, Hewlett-Packard Co. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +static struct max8903b_platform_data *pdevice_resource; +static enum max8903b_current current_limit; + +static int max8903b_current_setup(enum max8903b_current value) +{ + int rc = 0; + + enum max8903b_current old_current_limit = current_limit; + + current_limit = value; + + switch (value) { + case CHARGE_DISABLE: + /* disable charging */ + gpio_set_value(pdevice_resource->CEN_N_in, pdevice_resource->CEN_N_in_polarity ? 0 : 1); /* charger disable */ + printk(KERN_INFO "%s: ### CHARGE_DISABLE\n", __func__); + break; + case CURRENT_ZERO: // this is for no charger connection. + gpio_set_value(pdevice_resource->DCM_in, pdevice_resource->DCM_in_polarity ? 1 : 0); /* usb mode */ + gpio_set_value(pdevice_resource->USUS_in, pdevice_resource->USUS_in_polarity ? 0 : 1); /* usb suspend */ + pdevice_resource->suspend_gpio_config(); + printk(KERN_INFO "%s: CURRENT_ZERO\n", __func__); + break; + case CURRENT_100MA: + gpio_set_value(pdevice_resource->DCM_in, pdevice_resource->DCM_in_polarity? 1 : 0); /* usb mode */ + gpio_set_value(pdevice_resource->IUSB_in, pdevice_resource->IUSB_in_polarity ? 1 : 0);/* usb 100mA */ + gpio_set_value(pdevice_resource->USUS_in, pdevice_resource->USUS_in_polarity ? 1 : 0); + gpio_set_value(pdevice_resource->CEN_N_in, pdevice_resource->CEN_N_in_polarity ? 1 : 0); /* charger enable */ + printk(KERN_INFO "%s: CURRENT_100MA\n", __func__); + break; + case CURRENT_500MA: + gpio_set_value(pdevice_resource->DCM_in, pdevice_resource->DCM_in_polarity ? 1 : 0); /* usb mode */ + gpio_set_value(pdevice_resource->IUSB_in, pdevice_resource->IUSB_in_polarity ? 0 : 1); /* usb 500mA */ + gpio_set_value(pdevice_resource->USUS_in, pdevice_resource->USUS_in_polarity ? 1 : 0); + gpio_set_value(pdevice_resource->CEN_N_in, pdevice_resource->CEN_N_in_polarity ? 1 : 0); /* charger enable */ + printk(KERN_INFO "%s: CURRENT_500MA\n", __func__); + break; + case CURRENT_750MA: + case CURRENT_900MA: + case CURRENT_1000MA: + case CURRENT_1400MA: + case CURRENT_1500MA: + case CURRENT_2000MA: + gpio_set_value(pdevice_resource->DCM_in, pdevice_resource->DCM_in_polarity ? 0 : 1); /* DC mode */ + if (pdevice_resource->set_DC_CHG_Mode_current) { + if (pdevice_resource->set_DC_CHG_Mode_current(value) != 0) { + current_limit = old_current_limit; + } + } else { + printk(KERN_NOTICE "%s: set_DC_CHG_Mode_current is NULL\n", __func__); + } + gpio_set_value(pdevice_resource->CEN_N_in, pdevice_resource->CEN_N_in_polarity ? 1 : 0); /* charger enable */ + printk(KERN_INFO "%s: CURRENT_750(4), 900(5), 1000(6), 1400(7), 2000MA(9): %d\n", __func__, value); + break; + default: + current_limit = old_current_limit; + printk(KERN_INFO "%s: Not supported current setting\n", __func__); + rc = -EINVAL; + break; + } + + return rc; +} + + +static void max8903b_hw_init() +{ + gpio_set_value(pdevice_resource->DCM_in, pdevice_resource->DCM_in_polarity ? 1 : 0); /* usb mode */ + gpio_set_value(pdevice_resource->IUSB_in, pdevice_resource->IUSB_in_polarity ? 0 : 1); /* usb 500mA */ + gpio_set_value(pdevice_resource->USUS_in, pdevice_resource->USUS_in_polarity ? 1 : 0); + gpio_set_value(pdevice_resource->CEN_N_in, pdevice_resource->CEN_N_in_polarity ? 1 : 0); /* charger enable */ +} + +/* /sys/power/charger/status */ +/* + * - DC-input OK + * - Charge in progress + * - Charge Done + * - Charge stop w/Fault + */ + +/* /sys/power/charger/charging */ +/* + * Show the current current limit setting. + */ +static ssize_t show_currentlimit(struct device *dev, struct device_attribute *attr, + char *buf) +{ + char *str; + + //printk(KERN_INFO "show_currentlimit! \n"); + + switch (current_limit) { + case CHARGE_DISABLE: + str = "none"; break; + case CURRENT_ZERO: + str = "current0ma"; break; + case CURRENT_100MA: + str = "current100ma"; break; + case CURRENT_500MA: + str = "current500ma"; break; + case CURRENT_750MA: + str = "current750ma"; break; + case CURRENT_900MA: + str = "current900ma"; break; + case CURRENT_1000MA: + str = "current1000ma"; break; + case CURRENT_1400MA: + str = "current1400ma"; break; + case CURRENT_1500MA: + str = "current1500ma"; break; + case CURRENT_2000MA: + str = "current2000ma"; break; + default: + return 0; + } + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + + +/** + * Parse the input for a charging command and optional current limit. + */ +static ssize_t store_currentlimit(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + enum max8903b_current value; + //printk(KERN_INFO "store_currentlimit! \n"); + + if (strncmp(buf, "off", 3) == 0 || strncmp(buf, "none", 4) == 0) { + value = CHARGE_DISABLE; + } + else if (strncmp(buf, "current0ma", 10) == 0) { + value = CURRENT_ZERO; + } + else if (strncmp(buf, "current100ma", 12) == 0) { + value = CURRENT_100MA; + } + else if (strncmp(buf, "current500ma", 12) == 0) { + value = CURRENT_500MA; + } + else if (strncmp(buf, "current750ma", 12) == 0) { + value = CURRENT_750MA; + } + else if (strncmp(buf, "current900ma", 12) == 0) { + value = CURRENT_900MA; + } + else if (strncmp(buf, "current1000ma", 13) == 0) { + value = CURRENT_1000MA; + } + else if (strncmp(buf, "current1400ma", 13) == 0) { + value = CURRENT_1400MA; + } + else if (strncmp(buf, "current1500ma", 13) == 0) { + value = CURRENT_1500MA; + } + else if (strncmp(buf, "current2000ma", 13) == 0) { + value = CURRENT_2000MA; + } + else { + printk(KERN_INFO "Invalid charging command: %s\n", buf); + return 0; + } + + max8903b_current_setup(value); + + return count; +} + +void max8903b_set_charge_ma (unsigned ma) +{ + enum max8903b_current value; + + switch (ma){ + case 0: + value = CURRENT_ZERO; break; + case 100: + value = CURRENT_100MA; break; + case 500: + value = CURRENT_500MA; break; + case 750: + value = CURRENT_750MA; break; + case 900: + value = CURRENT_900MA; break; + case 1000: + value = CURRENT_1000MA; break; + case 1400: + value = CURRENT_1400MA; break; + case 1500: + value = CURRENT_1500MA; break; + case 2000: + value = CURRENT_2000MA; break; + default: + printk(KERN_INFO "%s: Invalid value: %d\n", __func__, ma); + return; + } + + if (current_limit != value) max8903b_current_setup(value); + + return; +} +EXPORT_SYMBOL (max8903b_set_charge_ma); + +void max8903b_disable_charge() +{ + max8903b_current_setup(CHARGE_DISABLE); +} +EXPORT_SYMBOL (max8903b_disable_charge); + + +/* /sys/power/charger/chargerstatus */ +/* + * Show the current current limit setting. + */ +static ssize_t show_chargerstatus(struct device *dev, struct device_attribute *attr, + char *buf) +{ + char *str; + int dc_ok, fault, status; + + if ( (dc_ok = gpio_get_value(pdevice_resource->DOK_N_out)) != 0) /* invalid DC-In */ + str = "InvalidDC"; /* invalid DC-in */ + else if ( (fault = gpio_get_value(pdevice_resource->FLT_N_out)) == 0) + str = "fault"; /* charger stopped with fault */ + else if ( (status = gpio_get_value(pdevice_resource->CHG_N_out)) == 0) + str = "progress"; /* charge in progress */ + else + str = "completed"; /* charge completed */ + + dc_ok = gpio_get_value(pdevice_resource->DOK_N_out); + fault = gpio_get_value(pdevice_resource->FLT_N_out); + status = gpio_get_value(pdevice_resource->CHG_N_out); + +// printk(KERN_INFO "chargerstatus %s! | DC_OK=%d, STAT=%d, FAULT=%d\n", +// str, dc_ok, status, fault); + + return snprintf(buf, PAGE_SIZE, "%s\n", str); +} + +static DEVICE_ATTR(currentlimit, S_IRUGO | S_IWUSR, show_currentlimit, store_currentlimit); +static DEVICE_ATTR(chargerstatus, S_IRUGO, show_chargerstatus, NULL); + + +static struct attribute *max8903b_power_sysfs_entries[] = { + &dev_attr_currentlimit.attr, + &dev_attr_chargerstatus.attr, + NULL +}; + +static struct attribute_group max8903b_power_attr_group = { + .name = NULL, + .attrs = max8903b_power_sysfs_entries +}; + + +static int __devinit max8903b_charger_probe(struct platform_device *pdev) +{ + struct max8903b_platform_data *pdata; + int ret; + + pdata = pdev->dev.platform_data; + + pdevice_resource = pdata; + + pdata->request_release_gpios(1); + + ret = sysfs_create_group(&pdev->dev.kobj, &max8903b_power_attr_group); + if (ret) { + dev_err(&pdev->dev, "failed to create sysfs 'charger' entries\n"); + } else { + ret = sysfs_create_link(power_kobj, &pdev->dev.kobj, "charger"); + if (ret) + dev_err(&pdev->dev, "failed to create sysfs 'charger' link\n"); + } + + //max8903b_hw_init(); + + return 0; +} + +static int __devexit max8903b_charger_remove(struct platform_device *pdev) +{ + struct max8903b_platform_data *pdata; + pdata = pdev->dev.platform_data; + pdata->request_release_gpios(0); + return 0; +} + +#ifdef CONFIG_PM +static int max8903b_resume(struct platform_device *pdev) +{ + int rc = 0; + struct max8903b_platform_data *pdata; + + pdata = pdev->dev.platform_data; + + printk("%s: resume\n", __func__); + rc = pdata->request_release_gpios(1); + + return rc; +} + +static int max8903b_suspend(struct platform_device *pdev, pm_message_t state) +{ + int rc = 0; + struct max8903b_platform_data *pdata; + + pdata = pdev->dev.platform_data; + + printk("%s: suspend\n", __func__); + rc = pdata->request_release_gpios(0); + + return rc; +} +#endif + +static struct platform_driver max8903b_charger_driver = { + .probe = max8903b_charger_probe, + .remove = __devexit_p(max8903b_charger_remove), + .driver = { + .name = "max8903b_chg", + .owner = THIS_MODULE, + }, +#ifdef CONFIG_PM + .suspend = max8903b_suspend, + .resume = max8903b_resume, +#endif +}; + +static int __init max8903b_charger_init(void) +{ + int rc; + + rc = platform_driver_register(&max8903b_charger_driver); + if (rc ==0) + printk(KERN_INFO "max8903b driver registeration! rc = %d\n", rc); + else + printk(KERN_DEBUG "NO max8903b driver registeration!"); + + return rc; +} + +static void __exit max8903b_charger_exit(void) +{ + platform_driver_unregister(&max8903b_charger_driver); +} + +module_init(max8903b_charger_init); +module_exit(max8903b_charger_exit); + +MODULE_DESCRIPTION("MAX8903B battery charger driver"); +MODULE_AUTHOR("Kyoung Kim "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/max8903b_charger.h b/include/linux/max8903b_charger.h new file mode 100644 index 00000000000..30a7abf8963 --- /dev/null +++ b/include/linux/max8903b_charger.h @@ -0,0 +1,38 @@ +#ifndef _LINUX_MAX8903B_CHARGER_H +#define _LINUX_MAX8903B_CHARGER_H + +enum max8903b_current { + CHARGE_DISABLE, + CURRENT_ZERO, + CURRENT_100MA, + CURRENT_500MA, + CURRENT_750MA, + CURRENT_900MA, + CURRENT_1000MA, + CURRENT_1400MA, + CURRENT_1500MA, + CURRENT_2000MA, +}; + + +struct max8903b_platform_data { + int DCM_in; + int DCM_in_polarity; + int IUSB_in; + int IUSB_in_polarity; + int USUS_in; + int USUS_in_polarity; + int CEN_N_in; + int CEN_N_in_polarity; + int DOK_N_out; + int CHG_N_out; + int FLT_N_out; + int (*set_DC_CHG_Mode_current)(enum max8903b_current value); + int (*request_release_gpios)(int request); + void (*suspend_gpio_config)(void); +}; + +void max8903b_set_charge_ma (unsigned ma); +void max8903b_disable_charge (void); + +#endif