static enum charge_level_t charger_detect(void) { int ret; u8 hw_status; u8 vbus_status; ret = twl6030_hw_status(&hw_status); if (ret) { printf("Failed to read hw_status, reason: %d\n", ret); return CHARGE_DISABLE; } ret = twl6030_vbus_status(&vbus_status); if (ret) { printf("Failed to read vbus_status, reason: %d\n", ret); return CHARGE_DISABLE; } printf("hw_status 0x%02x vbus_status 0x%02x\n", hw_status, vbus_status); if ((vbus_status & VBUS_DET) && !(hw_status & STS_USB_ID)) { return omap4_charger_detect(); } else { // No charger detected return CHARGE_DISABLE; } }
static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; int status; u8 vbus_state, hw_state; unsigned charger_type; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, CONTROLLER_STAT1); vbus_state = vbus_state & VBUS_DET; /* Ignore charger events other than VBUS */ if (vbus_state == twl->prev_vbus) return IRQ_HANDLED; if ((vbus_state) && !(hw_state & STS_USB_ID)) { regulator_enable(twl->usb3v3); charger_type = omap4_charger_detect(); if ((charger_type == POWER_SUPPLY_TYPE_USB_CDP) || (charger_type == POWER_SUPPLY_TYPE_USB)) { status = USB_EVENT_VBUS; twl->otg.default_a = false; twl->asleep = 1; twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = status; twl->otg.last_event = status; } else if (charger_type == POWER_SUPPLY_TYPE_USB_DCP) { regulator_disable(twl->usb3v3); status = USB_EVENT_CHARGER; twl->usb_cinlimit_mA = 1800; twl->otg.last_event = status; } else { regulator_disable(twl->usb3v3); goto vbus_notify; } atomic_notifier_call_chain(&twl->otg.notifier, status, &charger_type); } if (!vbus_state) { status = USB_EVENT_NONE; twl->linkstat = status; twl->otg.last_event = status; if (twl->asleep) { atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); regulator_disable(twl->usb3v3); twl->asleep = 0; } } sysfs_notify(&twl->dev->kobj, NULL, "vbus"); vbus_notify: twl->prev_vbus = vbus_state; return IRQ_HANDLED; }
static void linkstat_work(struct work_struct *work) { u8 vbus_state, hw_state; int status = USB_EVENT_NONE; unsigned charger_type; struct archos_twl6030_usb *twl = container_of(work, struct archos_twl6030_usb, work.work); hw_state = archos_twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); vbus_state = archos_twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, CONTROLLER_STAT1); //printk("hw_state: %x\n", hw_state); //printk("vbus_state: %x\n", vbus_state); /* force transition to floating mode if state before is unknown */ if (twl->state == STATE_UNKNOWN) { printk("STATE_UNKNOWN -> STATE_NONE\n"); twl->state = STATE_NONE; twl->otg.last_event = status; if (twl->asleep) { regulator_disable(twl->usb3v3); twl->asleep = 0; } atomic_notifier_call_chain(&twl->otg.notifier, USB_EVENT_NONE, twl->otg.gadget); } /* if (twl->state == STATE_VBUS_TRANS) { if (!(vbus_state & VBUS_DET)) { twl->state = STATE_NONE; archos_twl6030_phy_shutdown(&twl->otg); if (twl->asleep) { regulator_disable(twl->usb3v3); twl->asleep = 0; } } else if (vbus_state & CHRG_DET_N) { printk("STATE_VBUS_TRANS -> STATE_CHARGER\n"); twl->state = STATE_CHARGER; switch_set_state(&twl->usb_switch, twl->state); sysfs_notify(&twl->dev->kobj, NULL, "vbus"); } else { printk("STATE_VBUS_TRANS -> STATE_VBUS\n"); twl->otg.state = OTG_STATE_B_IDLE; twl->state = STATE_VBUS; twl->linkstat = USB_EVENT_VBUS; atomic_notifier_call_chain(&twl->otg.notifier, USB_EVENT_VBUS, twl->otg.gadget); switch_set_state(&twl->usb_switch, twl->state); sysfs_notify(&twl->dev->kobj, NULL, "vbus"); } } */ if (twl->state == STATE_VBUS || twl->state == STATE_CHARGER) { if (!(vbus_state & VBUS_DET)) { enum state state = twl->state; printk("STATE_VBUS -> STATE_NONE\n"); twl->state = STATE_NONE; twl->linkstat = USB_EVENT_NONE; twl->otg.last_event = status; atomic_notifier_call_chain(&twl->otg.notifier, USB_EVENT_NONE, twl->otg.gadget); if (twl->asleep) { regulator_disable(twl->usb3v3); twl->asleep = 0; } switch_set_state(&twl->usb_switch, twl->state); sysfs_notify(&twl->dev->kobj, NULL, "vbus"); archos_twl6030_phy_shutdown(&twl->otg); } } else if (twl->state == STATE_GND) { if (!(hw_state & STS_USB_ID)) { printk("STATE_GND -> STATE_NONE\n"); archos_twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_CLR); archos_twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET); twl->state = STATE_NONE; twl->otg.last_event = status; atomic_notifier_call_chain(&twl->otg.notifier, USB_EVENT_NONE, twl->otg.gadget); switch_set_state(&twl->usb_switch, twl->state); if (twl->asleep) { regulator_disable(twl->usb3v3); twl->asleep = 0; } } } else { if (hw_state & STS_USB_ID) { printk("STATE_NONE -> STATE_GND\n"); archos_twl6030_usb_ldo_init(twl); regulator_enable(twl->usb3v3); twl->asleep = 1; archos_twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_CLR); archos_twl6030_writeb(twl, TWL_MODULE_USB, 0x10, USB_ID_INT_EN_HI_SET); twl->state = STATE_GND; twl->otg.last_event = USB_EVENT_ID; twl->otg.default_a = true; twl->otg.state = OTG_STATE_A_IDLE; atomic_notifier_call_chain(&twl->otg.notifier, USB_EVENT_ID, twl->otg.gadget); switch_set_state(&twl->usb_switch, twl->state); } else if (vbus_state & VBUS_DET) { archos_twl6030_usb_ldo_init(twl); regulator_enable(twl->usb3v3); //archos_twl6030_phy_init(&twl->otg); twl->asleep = 1; //twl->state = STATE_VBUS_TRANS; charger_type = omap4_charger_detect(); if ((charger_type == POWER_SUPPLY_TYPE_USB_CDP) || (charger_type == POWER_SUPPLY_TYPE_USB)) { printk("STATE_NONE -> STATE_VBUS\n"); status = USB_EVENT_VBUS; twl->otg.last_event = status; twl->otg.default_a = false; twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = USB_EVENT_VBUS; twl->state = STATE_VBUS; twl->usb_cinlimit_mA = 500; } else if (charger_type == POWER_SUPPLY_TYPE_USB_DCP) { printk("STATE_NONE -> STATE_CHARGER\n"); status = USB_EVENT_CHARGER; twl->otg.last_event = status; twl->usb_cinlimit_mA = 1500; twl->state = STATE_CHARGER; } twl->prev_vbus = (vbus_state & VBUS_DET); switch_set_state(&twl->usb_switch, twl->state); atomic_notifier_call_chain(&twl->otg.notifier, status, &charger_type); sysfs_notify(&twl->dev->kobj, NULL, "vbus"); } } archos_twl6030_writeb(twl, TWL_MODULE_USB, status, USB_ID_INT_LATCH_CLR); twl->linkstat = status; if (twl->state == STATE_GND) schedule_delayed_work(&twl->work, HZ/10); }
static void twl6030_usb_irq_work(struct work_struct *work) { struct twl6030_usb *twl = container_of(work, struct twl6030_usb, usb_irq_work); int status; u8 vbus_state, hw_state, misc2_data; unsigned charger_type; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, CONTROLLER_STAT1); vbus_state = vbus_state & VBUS_DET; /* Ignore charger events other than VBUS */ if (vbus_state == twl->prev_vbus) return; if ((vbus_state) && !(hw_state & STS_USB_ID)) { /* Program MISC2 register and set bit VUSB_IN_VBAT */ misc2_data = twl6030_readb(twl, TWL6030_MODULE_ID0, TWL6030_MISC2); misc2_data |= 0x10; twl6030_writeb(twl, TWL6030_MODULE_ID0, misc2_data, TWL6030_MISC2); regulator_enable(twl->usb3v3); twl6030_phy_suspend(&twl->otg, 0); charger_type = omap4_charger_detect(); twl6030_phy_suspend(&twl->otg, 1); if ((charger_type == POWER_SUPPLY_TYPE_USB_CDP) || (charger_type == POWER_SUPPLY_TYPE_USB)) { status = USB_EVENT_VBUS; twl->otg.default_a = false; twl->asleep = 1; twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = status; twl->otg.last_event = status; } else if (charger_type == POWER_SUPPLY_TYPE_USB_DCP) { regulator_disable(twl->usb3v3); status = USB_EVENT_CHARGER; twl->usb_cinlimit_mA = 1800; twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = status; twl->otg.last_event = status; } else { regulator_disable(twl->usb3v3); goto vbus_notify; } atomic_notifier_call_chain(&twl->otg.notifier, status, &charger_type); } if (!vbus_state) { status = USB_EVENT_NONE; twl->linkstat = status; twl->otg.last_event = status; atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); if (twl->asleep) { regulator_disable(twl->usb3v3); twl->asleep = 0; /* Program MISC2 register and clear bit VUSB_IN_VBAT */ misc2_data = twl6030_readb(twl, TWL6030_MODULE_ID0, TWL6030_MISC2); misc2_data &= 0xEF; twl6030_writeb(twl, TWL6030_MODULE_ID0, misc2_data, TWL6030_MISC2); } } vbus_notify: sysfs_notify(&twl->dev->kobj, NULL, "vbus"); twl->prev_vbus = vbus_state; }
static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; int status; u8 vbus_state, hw_state, misc2_data; unsigned charger_type; hw_state = twl6030_readb(twl, TWL6030_MODULE_ID0, STS_HW_CONDITIONS); vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, CONTROLLER_STAT1); vbus_state = vbus_state & VBUS_DET; if((vbus_state) && (!is_mhl_initialized())){ hw_state = 0; } /* Ignore charger events other than VBUS */ if (vbus_state == twl->prev_vbus) return IRQ_HANDLED; wake_lock(&twl_lock); if ((vbus_state) && !(hw_state & STS_USB_ID)) { msleep(500); /* Program MISC2 register and set bit VUSB_IN_VBAT */ misc2_data = twl6030_readb(twl, TWL6030_MODULE_ID0, TWL6030_MISC2); misc2_data |= 0x10; twl6030_writeb(twl, TWL6030_MODULE_ID0, misc2_data, TWL6030_MISC2); regulator_enable(twl->usb3v3); twl6030_phy_suspend(&twl->otg, 0); charger_type = omap4_charger_detect(); twl6030_phy_suspend(&twl->otg, 1); if ((charger_type == POWER_SUPPLY_TYPE_USB_CDP) || (charger_type == POWER_SUPPLY_TYPE_USB)) { status = USB_EVENT_VBUS; twl->otg.default_a = false; twl->asleep = 1; twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = status; twl->otg.last_event = status; plugin_stat = PLUGIN_USB_CHARGER; dev_info(twl->dev, " %s USB_EVENT_VBUS\n",__func__); } else if (charger_type == POWER_SUPPLY_TYPE_USB_DCP) { regulator_disable(twl->usb3v3); status = USB_EVENT_CHARGER; twl->usb_cinlimit_mA = 1800; twl->otg.state = OTG_STATE_B_IDLE; twl->linkstat = status; twl->otg.last_event = status; plugin_stat = PLUGIN_AC_CHARGER; dev_info(twl->dev, " %s USB_EVENT_CHARGER\n",__func__); } else { regulator_disable(twl->usb3v3); goto vbus_notify; } atomic_notifier_call_chain(&twl->otg.notifier, status, &charger_type); } if (!vbus_state) { status = USB_EVENT_NONE; twl->linkstat = status; twl->otg.last_event = status; plugin_stat = PLUGIN_DEVICE_NONE; dev_info(twl->dev, " %s USB_EVENT_NONE\n",__func__); atomic_notifier_call_chain(&twl->otg.notifier, status, twl->otg.gadget); if (twl->asleep) { printk("<Du Wei> Turn off USB LDO\n"); regulator_disable(twl->usb3v3); twl->asleep = 0; /* Program MISC2 register and clear bit VUSB_IN_VBAT */ misc2_data = twl6030_readb(twl, TWL6030_MODULE_ID0, TWL6030_MISC2); misc2_data &= 0xEF; twl6030_writeb(twl, TWL6030_MODULE_ID0, misc2_data, TWL6030_MISC2); } } vbus_notify: sysfs_notify(&twl->dev->kobj, NULL, "vbus"); twl->prev_vbus = vbus_state; wake_unlock(&twl_lock); return IRQ_HANDLED; }