zte's code,first commit
Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/ap/os/linux/linux-3.4.x/drivers/power/sgm40561_charger.c b/ap/os/linux/linux-3.4.x/drivers/power/sgm40561_charger.c
new file mode 100644
index 0000000..dd60969
--- /dev/null
+++ b/ap/os/linux/linux-3.4.x/drivers/power/sgm40561_charger.c
@@ -0,0 +1,1003 @@
+/*
+ * Driver for the TI zx234502 battery charger.
+ *
+ * Author: Mark A. Greer <mgreer@animalcreek.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/power_supply.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+//#include <linux/mutex.h>
+#include <linux/semaphore.h>
+
+#include <linux/power/sgm40561_charger.h>
+#include <linux/mfd/zx234290.h>
+
+#include <mach/gpio.h>
+#include <mach/pcu.h>
+#include <mach/zx29_usb.h>
+#include <linux/workqueue.h>
+
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <asm/uaccess.h>
+#include <linux/sched.h>
+
+#define USB_IN GPIO_LOW
+#define USB_OUT GPIO_HIGH
+#define CHG_START GPIO_LOW
+#define CHG_STOP GPIO_HIGH
+
+/*
+ * The FAULT register is latched by the zx234502 (except for NTC_FAULT)
+ * so the first read after a fault returns the latched value and subsequent
+ * reads return the current value. In order to return the fault status
+ * to the user, have the interrupt handler save the reg's value and retrieve
+ * it in the appropriate health/status routine. Each routine has its own
+ * flag indicating whether it should use the value stored by the last run
+ * of the interrupt handler or do an actual reg read. That way each routine
+ * can report back whatever fault may have occured.
+ */
+struct sgm40561_dev_info {
+ struct device *dev;
+ struct power_supply charger;
+ struct power_supply battery;
+
+ kernel_ulong_t model;
+ unsigned int chgin_irq;
+ unsigned int chgstate_irq;
+ struct task_struct *chgin_irq_thread;
+ struct task_struct *chgstate_irq_thread;
+
+ struct sgm40561_platform_data *pdata;
+ unsigned int chgin_type;
+ unsigned int chgstate_type;
+ bool chg_en;
+ unsigned int chg_state;
+ struct semaphore chgin_sem;
+ struct semaphore chgstate_sem;
+ int stopchg_flag;
+};
+
+volatile struct sgm40561_dev_info *g_bdi = NULL;
+
+enum
+{
+ CHG_TEMP_ERROR=0,
+ CHG_EN=1,
+ CHG_FULL_STOP=2,
+ CHG_DEFAULT,
+};
+
+
+/* Charger power supply property routines */
+
+static int sgm40561_charger_get_charge_type(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ val->intval = bdi->charger.type;
+ return 0;
+}
+
+static int sgm40561_charger_get_status(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ val->intval = bdi->chg_state;
+ return 0;
+}
+
+static int sgm40561_charger_get_health(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ return 0;
+}
+
+int sgm40561_charger_get_online(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ if (USB_IN==bdi->chgin_type) {
+ val->intval= 1;
+ } else {
+ val->intval=0;/*usb not insert*/
+ }
+
+ return 0;
+}EXPORT_SYMBOL (sgm40561_charger_get_online);
+
+
+static int sgm40561_charger_get_charger_enabled(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ val->intval = bdi->chg_en;
+
+ return 0;
+}
+static int sgm40561_charger_get_voltage_max(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ val->intval = 4200;
+
+ return 0;
+}
+
+static int sgm40561_charger_set_voltage(struct sgm40561_dev_info *bdi,const union power_supply_propval *val)
+{
+ return 0;
+}
+
+static int sgm40561_charger_set_charger_config(struct sgm40561_dev_info *bdi,const union power_supply_propval *val)
+{
+ int ret;
+ int gpio_state = 0;
+
+ if (CHG_EN==val->intval){
+ gpio_state= 0 ;/*gpio low en chg*/
+ printk("mmi start chg\n");
+ }
+ else if(CHG_TEMP_ERROR==val->intval){
+ bdi->chg_state = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ gpio_state= 1 ;/*gpio high stop chg*/
+ printk("mmi stop chg\n");
+ }
+ else if(CHG_FULL_STOP == val->intval)
+ {
+ bdi->chg_state = POWER_SUPPLY_STATUS_FULL;
+ gpio_state= 1 ;/*gpio high stop chg*/
+ printk("mmi full stop chg\n");
+ }
+ else
+ printk("mmi error flag\n");
+
+ bdi->chg_en =(!gpio_state);
+ bdi->stopchg_flag = val->intval;
+
+ ret = gpio_direction_output(bdi ->pdata->gpio_chgen, gpio_state);
+
+ return ret;
+}
+
+
+static int sgm40561_charger_get_property(struct power_supply *psy,enum power_supply_property psp, union power_supply_propval *val)
+{
+ struct sgm40561_dev_info *bdi = container_of(psy, struct sgm40561_dev_info, charger);
+ int ret;
+
+ //dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+ //pm_runtime_get_sync(bdi->dev);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PC1_AC2:
+ ret = sgm40561_charger_get_charge_type(bdi, val);
+ break;
+
+ case POWER_SUPPLY_PROP_STATUS:
+ ret = sgm40561_charger_get_status(bdi, val);
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = sgm40561_charger_get_health(bdi, val);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = sgm40561_charger_get_online(bdi, val);
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ ret = sgm40561_charger_get_voltage_max(bdi, val);
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_ENABLED:
+ ret = sgm40561_charger_get_charger_enabled(bdi, val);
+ break;
+ default:
+ ret = -ENODATA;
+ }
+
+ //pm_runtime_put_sync(bdi->dev);
+ return ret;
+}
+
+static int sgm40561_charger_set_property(struct power_supply *psy,enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct sgm40561_dev_info *bdi =
+ container_of(psy, struct sgm40561_dev_info, charger);
+ int ret;
+
+ //dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+ //pm_runtime_get_sync(bdi->dev);
+
+ switch (psp) {
+#if 0
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ ret = zx234502_charger_set_current(bdi, val);
+ break;
+#endif
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ ret = sgm40561_charger_set_voltage(bdi, val);
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_ENABLED:
+ ret = sgm40561_charger_set_charger_config(bdi, val);
+ //power_supply_changed(&bdi->charger);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ //pm_runtime_put_sync(bdi->dev);
+ return ret;
+}
+
+static int sgm40561_charger_property_is_writeable(struct power_supply *psy,enum power_supply_property psp)
+{
+ int ret;
+
+ switch (psp)
+ {
+ //case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ case POWER_SUPPLY_PROP_CHARGE_ENABLED:
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static enum power_supply_property sgm40561_charger_properties[] = {
+ POWER_SUPPLY_PROP_PC1_AC2,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_ONLINE,
+ //POWER_SUPPLY_PROP_CURRENT_NOW,
+ //POWER_SUPPLY_PROP_CURRENT_MAX,
+ //POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_CHARGE_ENABLED,
+};
+
+static char *sgm40561_charger_supplied_to[] = {
+ "main-battery",
+};
+
+static void sgm40561_charger_init(struct power_supply *charger)
+{
+ charger->name = "charger";
+ charger->type = POWER_SUPPLY_PCAC_UNKNOWN;
+ charger->properties = sgm40561_charger_properties;
+ charger->num_properties = ARRAY_SIZE(sgm40561_charger_properties);
+ charger->supplied_to = sgm40561_charger_supplied_to;
+ //charger->num_supplies = ARRAY_SIZE(sgm40561_charger_supplied_to);
+ charger->get_property = sgm40561_charger_get_property;
+ charger->set_property = sgm40561_charger_set_property;
+ charger->property_is_writeable = sgm40561_charger_property_is_writeable;
+}
+
+/* Battery power supply property routines */
+
+static int sgm40561_battery_get_health(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ val->intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ return 0;
+}
+
+static int sgm40561_battery_get_online(struct sgm40561_dev_info *bdi,union power_supply_propval *val)
+{
+ val->intval = 1;/*bat on*/
+
+ return 0;
+}
+
+static int sgm40561_battery_set_online(struct sgm40561_dev_info *bdi,const union power_supply_propval *val)
+{
+ return 0;
+}
+
+
+static int sgm40561_battery_get_property(struct power_supply *psy,enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sgm40561_dev_info *bdi =
+ container_of(psy, struct sgm40561_dev_info, battery);
+ int ret;
+
+ //dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+ //pm_runtime_get_sync(bdi->dev);
+
+ switch (psp) {
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ ret = sgm40561_battery_get_health(bdi, val);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = sgm40561_battery_get_online(bdi, val);
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = get_adc2_voltage();
+ ret = 0;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ //#ifdef CONFIG_ZX234290_ADC
+ val->intval = get_adc1_voltage() - 15;
+ ret = 0;
+ break;
+ default:
+ ret = -ENODATA;
+ }
+
+ //pm_runtime_put_sync(bdi->dev);
+ return ret;
+}
+
+static int sgm40561_battery_set_property(struct power_supply *psy,enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct sgm40561_dev_info *bdi =
+ container_of(psy, struct sgm40561_dev_info, battery);
+ int ret;
+
+ //dev_dbg(bdi->dev, "prop: %d\n", psp);
+
+ //pm_runtime_put_sync(bdi->dev);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = sgm40561_battery_set_online(bdi, val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ //pm_runtime_put_sync(bdi->dev);
+ return ret;
+}
+
+static int sgm40561_battery_property_is_writeable(struct power_supply *psy,enum power_supply_property psp)
+{
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = 1;
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static enum power_supply_property sgm40561_battery_properties[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ //POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static void sgm40561_battery_init(struct power_supply *battery)
+{
+ battery->name = "battery";
+ battery->type = POWER_SUPPLY_PCAC_UNKNOWN;
+ battery->properties = sgm40561_battery_properties;
+ battery->num_properties = ARRAY_SIZE(sgm40561_battery_properties);
+ battery->get_property = sgm40561_battery_get_property;
+ battery->set_property = sgm40561_battery_set_property;
+ battery->property_is_writeable = sgm40561_battery_property_is_writeable;
+}
+
+
+static irqreturn_t sgm40561_charger_in_irq_primary_handler(int irq, struct sgm40561_dev_info * bdi)
+{
+ disable_irq_nosync(irq);
+ //pcu_int_clear(irq);
+ pcu_clr_irq_pending(irq);
+ up(&bdi->chgin_sem);
+
+ return IRQ_HANDLED;
+}
+static irqreturn_t sgm40561_charger_state_irq_primary_handler(int irq, struct sgm40561_dev_info * bdi)
+{
+ disable_irq_nosync(irq);
+ //pcu_int_clear(irq);
+ pcu_clr_irq_pending(irq);
+ up(&bdi->chgstate_sem);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sgm40561_chgin_irq_handler_thread(void *data)
+{
+ struct sgm40561_dev_info *bdi = data;
+ struct sgm40561_platform_data *pdata = bdi->pdata;
+ int g_gpio_in =0;
+ int ret;
+ struct sched_param param = { .sched_priority = 2 };
+
+ param.sched_priority= 31;
+ sched_setscheduler(current, SCHED_FIFO, ¶m);
+
+ while(1)
+ {
+ down(&bdi->chgin_sem);
+
+ //printk(KERN_INFO"sgm40561_chgin_irq_handler_thread\n");
+ /*not need*/
+ #if 0
+ zx29_gpio_config(pdata->gpio_chgin, pdata->gpio_chgin_gpio_sel);
+ gpio_direction_input(pdata->gpio_chgin);
+
+ mdelay(10);
+ #endif
+ g_gpio_in = gpio_get_value(pdata->gpio_chgin);
+#if 0
+ zx29_gpio_config(pdata->gpio_chgin,pdata->gpio_chgin_fun_sel);
+#endif
+ /*charging status*/
+ if (g_gpio_in == USB_IN){
+ if (bdi->chgin_type== USB_IN){
+ printk(KERN_INFO"chg usb in err\n");
+ }
+ else{
+ bdi->chgin_type = USB_IN;
+ printk(KERN_INFO"chg usb in\n");
+ dwc_otg_chg_inform(0);/*usb in*/
+ zx29_gpio_set_inttype(pdata->gpio_chgin, IRQ_TYPE_EDGE_RISING);
+ }
+ }
+ else if (g_gpio_in == USB_OUT){
+ bdi->chg_state=POWER_SUPPLY_STATUS_DISCHARGING;
+ if (bdi->chgin_type== USB_OUT){
+ printk(KERN_INFO"chg usb out err\n");
+ }
+ else{
+ bdi->chgin_type = USB_OUT;
+ SINT32 Usb_plug = DISCONNECTED_FROM_HOST;
+ printk(KERN_INFO"chg usb out\n");
+ dwc_otg_chg_inform(1);/*usb out*/
+
+ gpio_direction_output(bdi ->pdata->gpio_chgen, 0);/*gpio low enable chg*/
+ bdi->chg_en =1;/*chg enable*/
+
+ zx29_gpio_set_inttype(pdata->gpio_chgin, IRQ_TYPE_EDGE_FALLING);
+ }
+ }
+
+ //mdelay(30); //?
+
+ power_supply_changed(&bdi->charger);
+ power_supply_changed(&bdi->battery);
+
+ //pcu_clr_irq_pending((bdi->chgin_irq));
+ enable_irq(bdi->chgin_irq);
+
+ }
+
+ return 0;
+}
+
+static irqreturn_t sgm40561_chgstate_irq_handler_thread(void *data)
+{
+ struct sgm40561_dev_info *bdi = data;
+ struct sgm40561_platform_data *pdata = bdi->pdata;
+ int g_gpio_state=CHG_STOP;
+ int ret;
+ struct sched_param param = { .sched_priority = 2 };
+
+ param.sched_priority= 31;
+ sched_setscheduler(current, SCHED_FIFO, ¶m);
+
+ while(1)
+ {
+
+ down(&bdi->chgstate_sem);
+#if 0
+ zx29_gpio_config(pdata->gpio_chgstate, pdata->gpio_chgstate_gpio_sel);
+ gpio_direction_input(pdata->gpio_chgstate);
+
+ mdelay(30);
+#endif
+ g_gpio_state = gpio_get_value(pdata->gpio_chgstate);
+#if 0
+ zx29_gpio_config(pdata->gpio_chgstate,pdata->gpio_chgstate_fun_sel);
+#endif
+ /*charging status*/
+ if (g_gpio_state == CHG_START){ /*low charging*/
+ bdi->chg_state=POWER_SUPPLY_STATUS_CHARGING;
+
+ if (bdi->chgstate_type== CHG_START){
+ printk(KERN_INFO"chg chging err!\n");
+ }
+ else{
+ bdi->chgstate_type = CHG_START;
+ printk(KERN_INFO"chg charging\n");
+ zx29_gpio_set_inttype(pdata->gpio_chgstate, IRQ_TYPE_EDGE_RISING);
+ }
+ }
+ else if (g_gpio_state ==CHG_STOP){/*high stop charging*/
+ if (bdi->chgstate_type== CHG_STOP){
+ printk(KERN_INFO"chg stop err\n");
+ }
+ else if((bdi->stopchg_flag==CHG_TEMP_ERROR)&&(bdi->chgin_type== USB_IN)){
+ bdi->chg_state=POWER_SUPPLY_STATUS_NOT_CHARGING;
+ printk(KERN_INFO"chg temp stop\n");
+
+ }
+ else if (bdi->chgin_type== USB_IN){
+ bdi->chg_state=POWER_SUPPLY_STATUS_FULL;
+ printk(KERN_INFO"chg full \n");
+ }
+ else
+ bdi->chg_state=POWER_SUPPLY_STATUS_DISCHARGING;
+
+ bdi->chgstate_type = CHG_STOP;
+ printk(KERN_INFO"chg stop\n");
+ zx29_gpio_set_inttype(pdata->gpio_chgstate, IRQ_TYPE_EDGE_FALLING);
+ }
+
+ //mdelay(30); //?
+
+ power_supply_changed(&bdi->charger);
+ power_supply_changed(&bdi->battery);
+
+ enable_irq(bdi->chgstate_irq);
+
+ }
+ return 0;
+}
+
+
+static int sgm40561_setup_pdata(struct sgm40561_dev_info *bdi,
+ struct sgm40561_platform_data *pdata)
+{
+ int ret;
+
+ if (!gpio_is_valid(pdata->gpio_chgen))
+ return -1;
+
+ ret = gpio_request(pdata->gpio_chgen, "gpio_chgen");
+ if (ret < 0)
+ goto out;
+ ret = zx29_gpio_config(pdata->gpio_chgen, pdata->gpio_chgen_gpio_sel);
+ if (ret < 0)
+ goto out;
+ ret = gpio_direction_output(pdata->gpio_chgen,0);
+ if (ret < 0)
+ goto out;
+ bdi->chg_en =true;
+#if 0
+ //chg termination current ctrl
+ if (!gpio_is_valid(pdata->gpio_chgctrl))
+ return -1;
+
+ ret = gpio_request(pdata->gpio_chgctrl, "gpio_chgctrl");
+ if (ret < 0)
+ goto out;
+ ret = zx29_gpio_config(pdata->gpio_chgctrl, pdata->gpio_chgctrl_gpio_sel);
+ if (ret < 0)
+ goto out;
+ ret = gpio_direction_output(pdata->gpio_chgctrl, 0);
+ if (ret < 0)
+ goto out;
+#endif
+ return 0;
+out:
+ //gpio_free(pdata->gpio_int);
+ return -1;
+}
+
+
+static int sgm40561_init_state(struct sgm40561_dev_info *bdi)
+{
+ struct sgm40561_platform_data *pdata;
+ int ret = 0;
+ unsigned int g_gpio_in,g_gpio_state;
+ unsigned int chgin_irq_type=IRQ_TYPE_NONE;
+ unsigned int chgstate_irq_type=IRQ_TYPE_NONE;
+
+ pdata= bdi->pdata;
+
+ if (!gpio_is_valid(pdata->gpio_chgin))
+ goto error;
+
+ ret = gpio_request(pdata->gpio_chgin, "gpio_chgin");
+ if (ret < 0)
+ goto error;
+
+ zx29_gpio_pd_pu_set(pdata->gpio_chgin, IO_CFG_PULL_DISABLE);
+ #if 0
+ ret = zx29_gpio_config(pdata->gpio_chgin, pdata->gpio_chgin_gpio_sel);
+ if (ret < 0)
+ goto error;
+ #endif
+ ret = gpio_direction_input(pdata->gpio_chgin);
+ if (ret < 0)
+ goto error;
+
+ mdelay(20);/*?*/
+ g_gpio_in = gpio_get_value(pdata->gpio_chgin);
+
+ if ( USB_IN == g_gpio_in){
+ bdi->chgin_type = USB_IN;
+ printk(KERN_INFO"init usb in\n");
+ chgin_irq_type = IRQ_TYPE_EDGE_RISING;
+ dwc_otg_chg_inform(0);/*usb in*/
+ }
+ else if ( USB_OUT == g_gpio_in){
+ bdi->chgin_type = USB_OUT;
+ bdi->chg_state=POWER_SUPPLY_STATUS_DISCHARGING;
+ printk(KERN_INFO"init usb out\n");
+ chgin_irq_type = IRQ_TYPE_EDGE_FALLING;
+ dwc_otg_chg_inform(1);/*usb out*/
+ }
+
+ bdi->chgin_irq= gpio_to_irq(pdata->gpio_chgin);
+
+ ret = zx29_gpio_config(pdata->gpio_chgin,pdata->gpio_chgin_fun_sel);
+ if (ret < 0)
+ goto error;
+
+ zx29_gpio_set_inttype(pdata->gpio_chgin,chgin_irq_type); //INT_POSEDGE
+ pcu_clr_irq_pending(bdi->chgin_irq);
+
+ if (!gpio_is_valid(pdata->gpio_chgstate))
+ goto error;
+
+ ret = gpio_request(pdata->gpio_chgstate, "gpio_chgin");
+ if (ret < 0)
+ goto error;
+
+ zx29_gpio_pd_pu_set(pdata->gpio_chgstate, IO_CFG_PULL_DISABLE);
+
+ bdi->chgstate_irq= gpio_to_irq(pdata->gpio_chgstate);
+#if 0
+ ret = zx29_gpio_config(pdata->gpio_chgstate, pdata->gpio_chgstate_gpio_sel);
+ if (ret < 0)
+ goto error;
+#endif
+ ret = gpio_direction_input(pdata->gpio_chgstate);
+ if (ret < 0)
+ goto error;
+ mdelay(20);/*?*/
+ g_gpio_state = gpio_get_value(pdata->gpio_chgstate);
+ bdi->stopchg_flag = CHG_DEFAULT;
+
+ if (CHG_START == g_gpio_state){
+ bdi->chgstate_type=CHG_START ;
+ bdi->chg_state=POWER_SUPPLY_STATUS_CHARGING;
+
+ //printk(KERN_INFO"send ap usb in message\n");
+ chgstate_irq_type = IRQ_TYPE_EDGE_RISING;
+ //dwc_otg_chg_inform(0);/*usb in*/
+ }
+ else if (CHG_STOP == g_gpio_state){
+ bdi->chgstate_type =CHG_STOP ;
+
+ //printk(KERN_INFO"send ap usb out message\n");
+ chgstate_irq_type = IRQ_TYPE_EDGE_FALLING;
+ //dwc_otg_chg_inform(1);/*usb out*/
+ if( bdi->chgin_type == USB_IN)
+ bdi->chg_state=POWER_SUPPLY_STATUS_FULL;
+ else
+ bdi->chg_state=POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+
+ ret = zx29_gpio_config(pdata->gpio_chgstate,pdata->gpio_chgstate_fun_sel);
+ if (ret < 0)
+ goto error;
+
+ zx29_gpio_set_inttype(pdata->gpio_chgstate,chgstate_irq_type); //INT_POSEDGE
+
+ pcu_clr_irq_pending(bdi->chgstate_irq);
+
+error:
+ printk(KERN_INFO"chg gpio error ret = %d\n",ret);
+ return -1;
+}
+
+
+
+static void sgm40561_charge_typedet(T_TYPE_USB_DETECT chg_type)
+{
+ u8 ret;
+ #ifdef DBG_CHARGE
+ //printk(KERN_INFO"charge type is %d in\n",chg_type);
+ #endif
+
+ if(TYPE_ADAPTER == chg_type){
+ printk(KERN_INFO"chg type DC\n");
+ g_bdi->charger.type = POWER_SUPPLY_PCAC__AC;
+ }
+ else{
+ printk(KERN_INFO"chg type PC\n");
+ g_bdi->charger.type = POWER_SUPPLY_PCAC__PC;
+ }
+
+}
+
+
+#if defined(CONFIG_DEBUG_FS)
+static ssize_t debugfs_regs_write(struct file *file, const char __user *buf,size_t nbytes, loff_t *ppos)
+{
+ unsigned int val1, val2;
+ u8 reg, value;
+ int ret;
+ char *kern_buf;
+ struct seq_file *s = file->private_data;
+ struct sgm40561_dev_info *sgm40561 = s->private;
+
+ kern_buf = kzalloc(nbytes, GFP_KERNEL);
+
+ if (!kern_buf) {
+ printk(KERN_INFO "sgm40561_charger: Failed to allocate buffer\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(kern_buf, (void __user *)buf, nbytes)) {
+ kfree(kern_buf);
+ return -ENOMEM;
+ }
+ printk(KERN_INFO "%s input str=%s,nbytes=%d \n", __func__, kern_buf,nbytes);
+
+ ret = sscanf(kern_buf, "%x:%x", &val1, &val2);
+ if (ret < 2) {
+ printk(KERN_INFO "sgm40561_charger: failed to read user buf, ret=%d, input 0x%x:0x%x\n",
+ ret, val1, val2);
+ kfree(kern_buf);
+ return -EINVAL;
+ }
+
+ kfree(kern_buf);
+
+ return ret ? ret : nbytes;
+}
+
+static int debugfs_regs_show(struct seq_file *s, void *v)
+{
+ int i;
+ int ret=0;
+ int curr = 0;
+ struct sgm40561_dev_info *sgm40561 = s->private;
+
+ /*charger type*/
+ if(sgm40561->charger.type ==POWER_SUPPLY_PCAC__PC){
+ seq_printf(s, "charger type is PC\n");
+ }
+ else if(sgm40561->charger.type ==POWER_SUPPLY_PCAC__AC){
+ seq_printf(s, "charger type is AC\n");
+ }
+ else
+ seq_printf(s, "charger type is unknow = %d\n",sgm40561->charger.type);
+
+ seq_printf(s, "mmi charger config state = %d\n",sgm40561->chg_en);
+ seq_printf(s, "chg in state = %s\n",(sgm40561->chgin_type==USB_IN)? "USB_IN": "USB_OUT");
+ seq_printf(s, "chg_state state = %s\n",sgm40561->chgstate_type ? "CHG_STOP": "CHG_START");
+ seq_printf(s, "stop_chg_flag = %d\n",sgm40561->stopchg_flag);
+
+ return ret;
+}
+
+#define DEBUGFS_FILE_ENTRY(name) \
+static int debugfs_##name##_open(struct inode *inode, struct file *file) \
+{\
+return single_open(file, debugfs_##name##_show, inode->i_private); \
+}\
+\
+static const struct file_operations debugfs_##name##_fops = { \
+.owner= THIS_MODULE, \
+.open= debugfs_##name##_open, \
+.write=debugfs_##name##_write, \
+.read= seq_read, \
+.llseek= seq_lseek, \
+.release= single_release, \
+}
+
+DEBUGFS_FILE_ENTRY(regs);
+
+static struct dentry *g_charger_root;
+
+static void debugfs_charger_init(struct sgm40561_dev_info *sgm40561)
+{
+ struct dentry *root;
+ struct dentry *node;
+ int i;
+
+ if(!sgm40561)
+ return;
+
+ //create root
+ root = debugfs_create_dir("charger_zx29", NULL);
+ if (!root) {
+ dev_err(&sgm40561->dev, "debugfs_create_dir err=%d\n", IS_ERR(root));
+ goto err;
+ }
+
+ //print regs;
+ node = debugfs_create_file("regs", S_IRUGO | S_IWUGO, root, sgm40561, &debugfs_regs_fops);
+ if (!node){
+ dev_err(&sgm40561->dev, "debugfs_create_dir err=%d\n", IS_ERR(node));
+ goto err;
+ }
+
+ g_charger_root = (void *)root;
+ return;
+err:
+ dev_err(&sgm40561->dev, "debugfs_charger_init err\n");
+}
+
+#endif
+
+
+static int __devinit sgm40561_charger_probe(struct platform_device *pdev)
+{
+ struct sgm40561_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct sgm40561_dev_info *bdi;
+ int ret;
+
+ bdi = devm_kzalloc(dev, sizeof(*bdi), GFP_KERNEL);
+ if (!bdi) {
+ dev_err(dev, "Can't alloc bdi struct\n");
+ return -ENOMEM;
+ }
+ bdi->pdata = pdata;
+
+ //printk(KERN_INFO "charger probe.\n");
+
+ bdi->chg_state = POWER_SUPPLY_STATUS_UNKNOWN;
+ bdi->charger.type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ g_bdi = bdi;
+
+ ret = sgm40561_setup_pdata(bdi, pdata);
+
+ if (ret) {
+ dev_err(dev, "Can't get irq info\n");
+ return -EINVAL;
+ }
+
+ sgm40561_charger_init(&bdi->charger);
+
+ ret = power_supply_register(dev, &bdi->charger);
+ if (ret) {
+ dev_err(dev, "Can't register charger\n");
+ goto out2;
+ }
+ //printk(KERN_INFO "sgm40561_probe power_supply_register charger ok.\n");
+
+ sgm40561_battery_init(&bdi->battery);
+
+ ret = power_supply_register(dev, &bdi->battery);
+ if (ret) {
+ dev_err(dev, "Can't register battery\n");
+ goto out3;
+ }
+ //printk(KERN_INFO "sgm40561_probe power_supply_register battery ok.\n");
+ sema_init(&bdi->chgin_sem, 0);
+ sema_init(&bdi->chgstate_sem, 0);
+
+ dwc_chg_Regcallback(sgm40561_charge_typedet);/*register for usb*/
+ sgm40561_init_state(bdi);
+
+ ret = request_irq(bdi->chgin_irq, sgm40561_charger_in_irq_primary_handler,IRQF_NO_THREAD, "sgm40561-chgin", bdi);
+ if (ret < 0) {
+ dev_err(dev, "Can't set up irq handler\n");
+ if (bdi->pdata->gpio_chgin)
+ gpio_free(bdi->pdata->gpio_chgin);
+
+ goto out1;
+ }
+ irq_set_irq_wake(bdi->chgin_irq, 1);
+ bdi->chgin_irq_thread = kthread_run(sgm40561_chgin_irq_handler_thread, bdi, "sgm40561-chgin");
+ BUG_ON(IS_ERR(bdi->chgin_irq_thread));
+
+ ret = request_irq(bdi->chgstate_irq, sgm40561_charger_state_irq_primary_handler,IRQF_NO_THREAD, "sgm40561-chgstate", bdi);
+ if (ret < 0) {
+ dev_err(dev, "Can't set up irq handler\n");
+ if (bdi->pdata->gpio_chgstate)
+ gpio_free(bdi->pdata->gpio_chgin);
+
+ goto out1;
+ }
+ irq_set_irq_wake(bdi->chgstate_irq, 1);
+ bdi->chgstate_irq_thread = kthread_run(sgm40561_chgstate_irq_handler_thread, bdi, "sgm40561-chgstate");
+ BUG_ON(IS_ERR(bdi->chgstate_irq_thread));
+
+#if defined(CONFIG_DEBUG_FS)
+ debugfs_charger_init(bdi);
+#endif
+
+#ifdef DBG_CHARGE
+ //printk(KERN_INFO "sgm40561_probe end.\n");
+#endif
+
+ return 0;
+
+out3:
+ power_supply_unregister(&bdi->battery);
+out2:
+ //pm_runtime_disable(dev);
+ power_supply_unregister(&bdi->charger);
+out1:
+
+ return ret;
+
+}
+
+static int sgm40561_charger_remove(struct platform_device *pdev)
+{
+ struct sgm40561_platform_data *pdata = pdev->dev.platform_data;
+
+ power_supply_unregister(&(g_bdi->battery));
+ power_supply_unregister(&(g_bdi->charger));
+
+ pm_runtime_disable(g_bdi->dev);
+
+ if (pdata->gpio_chgctrl)
+ gpio_free(pdata->gpio_chgctrl);
+ if (pdata->gpio_chgen)
+ gpio_free(pdata->gpio_chgen);
+ if (pdata->gpio_chgin)
+ gpio_free(pdata->gpio_chgin);
+ if (pdata->gpio_chgstate)
+ gpio_free(pdata->gpio_chgstate);
+
+
+#if defined(CONFIG_DEBUG_FS)
+ if(g_charger_root){
+ //printk(KERN_INFO "sgm40561_device_exit:debugfs_remove_recursive \n");
+ debugfs_remove_recursive(g_charger_root);
+ }
+#endif
+
+ return 0;
+}
+
+
+static struct platform_driver zx29_charger_driver = {
+ .probe = sgm40561_charger_probe,
+ .remove = __devexit_p(sgm40561_charger_remove),
+ .driver = {
+ .name = "sgm40561-charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+//module_platform_driver(zx29_charger_driver);
+
+
+static int __init zx29_charger_init(void)
+{
+ return platform_driver_register(&zx29_charger_driver);
+}
+
+static void __exit zx29_charger_exit(void)
+{
+ platform_driver_unregister(&zx29_charger_driver);
+}
+
+module_init(zx29_charger_init);
+module_exit(zx29_charger_exit);
+
+
+MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
+MODULE_DESCRIPTION("SGM40561 Charger Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:zx29_charger");
+