From: Balaji T K <balajitk@xxxxxx> Charger detection is done in threaded irq and is performed only when OMAP acts as B-device. Phy is powered on before charger detection and is powered down once charger detection is completed. The type of charger is sent through all the registered notifiers. Signed-off-by: Balaji T K <balajitk@xxxxxx> Signed-off-by: Kishon Vijay Abraham I <kishon@xxxxxx> Signed-off-by: Hema HK <hemahk@xxxxxx> Signed-off-by: Partha Basak <p-basak2@xxxxxx> --- arch/arm/mach-omap2/omap_phy_internal.c | 50 ++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/usb.h | 1 + drivers/usb/otg/twl6030-usb.c | 51 ++++++++++++++++++++++-------- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-omap2/omap_phy_internal.c b/arch/arm/mach-omap2/omap_phy_internal.c index a828833..e5a6701 100644 --- a/arch/arm/mach-omap2/omap_phy_internal.c +++ b/arch/arm/mach-omap2/omap_phy_internal.c @@ -27,6 +27,7 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/usb.h> +#include <linux/power_supply.h> #include <plat/usb.h> #include "control.h" @@ -42,6 +43,13 @@ #define SESSEND BIT(3) #define IDDIG BIT(4) +#define CONTROL_USB2PHYCORE 0x620 +#define CHARGER_TYPE_PS2 0x2 +#define CHARGER_TYPE_DEDICATED 0x4 +#define CHARGER_TYPE_HOST 0x5 +#define CHARGER_TYPE_PC 0x6 +#define USB2PHY_CHGDETECTED BIT(13) + static struct clk *phyclk, *clk48m, *clk32k; static void __iomem *ctrl_base; static int usbotghs_control; @@ -107,6 +115,48 @@ int omap4430_phy_set_clk(struct device *dev, int on) return 0; } +int omap4_charger_detect(void) +{ + unsigned long timeout; + int charger = 0; + u32 usb2phycore = 0; + u32 chargertype = 0; + + omap4430_phy_power(NULL, 0, 1); + + timeout = jiffies + msecs_to_jiffies(500); + do { + usb2phycore = omap4_ctrl_pad_readl(CONTROL_USB2PHYCORE); + chargertype = ((usb2phycore >> 21) & 0x7); + if (usb2phycore & USB2PHY_CHGDETECTED) + break; + } while (!time_after(jiffies, timeout)); + + switch (chargertype) { + case CHARGER_TYPE_DEDICATED: + charger = POWER_SUPPLY_TYPE_USB_DCP; + pr_info("DCP detected\n"); + break; + case CHARGER_TYPE_HOST: + charger = POWER_SUPPLY_TYPE_USB_CDP; + pr_info("CDP detected\n"); + break; + case CHARGER_TYPE_PC: + charger = POWER_SUPPLY_TYPE_USB; + pr_info("PC detected\n"); + break; + case CHARGER_TYPE_PS2: + pr_info("PS/2 detected!\n"); + break; + default: + pr_err(KERN_ERR"Unknown charger detected! %d\n", chargertype); + } + + omap4430_phy_power(NULL, 0, 0); + + return charger; +} + int omap4430_phy_power(struct device *dev, int ID, int on) { if (on) { diff --git a/arch/arm/plat-omap/include/plat/usb.h b/arch/arm/plat-omap/include/plat/usb.h index 17d3c93..582851b 100644 --- a/arch/arm/plat-omap/include/plat/usb.h +++ b/arch/arm/plat-omap/include/plat/usb.h @@ -107,6 +107,7 @@ extern int omap4430_phy_power(struct device *dev, int ID, int on); extern int omap4430_phy_set_clk(struct device *dev, int on); extern int omap4430_phy_init(struct device *dev); extern int omap4430_phy_exit(struct device *dev); +extern int omap4_charger_detect(void); extern int omap4430_phy_suspend(struct device *dev, int suspend); #endif diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c index 843c47d..6cb28ea 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -32,6 +32,8 @@ #include <linux/notifier.h> #include <linux/slab.h> #include <linux/delay.h> +#include <plat/usb.h> +#include <linux/power_supply.h> /* usb register definitions */ #define USB_VENDOR_ID_LSB 0x00 @@ -102,6 +104,7 @@ struct twl6030_usb { int irq2; u8 linkstat; u8 asleep; + u8 prev_vbus; bool irq_enabled; bool vbus_enable; unsigned long features; @@ -273,6 +276,7 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) { struct twl6030_usb *twl = _twl; struct usb_otg *otg = twl->xceiv.otg; + unsigned charger_type; int status; u8 vbus_state, hw_state; @@ -280,29 +284,48 @@ static irqreturn_t twl6030_usb_irq(int irq, void *_twl) vbus_state = twl6030_readb(twl, TWL_MODULE_MAIN_CHARGE, CONTROLLER_STAT1); - if (!(hw_state & STS_USB_ID)) { - if (vbus_state & VBUS_DET) { - regulator_enable(twl->usb3v3); - twl->asleep = 1; + 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; otg->default_a = false; + twl->asleep = 1; twl->xceiv.state = USB_PHY_STATE_B_IDLE; twl->linkstat = status; twl->xceiv.last_event = status; - atomic_notifier_call_chain(&twl->xceiv.notifier, - status, otg->gadget); - } else { - status = USB_EVENT_NONE; - twl->linkstat = status; + } else if (charger_type == POWER_SUPPLY_TYPE_USB_DCP) { + regulator_disable(twl->usb3v3); + status = USB_EVENT_CHARGER; twl->xceiv.last_event = status; + } else { + regulator_disable(twl->usb3v3); + goto vbus_notify; + } + atomic_notifier_call_chain(&twl->xceiv.notifier, + status, &charger_type); + } + if (!vbus_state) { + status = USB_EVENT_NONE; + twl->linkstat = status; + twl->xceiv.last_event = status; + if (twl->asleep) { atomic_notifier_call_chain(&twl->xceiv.notifier, - status, otg->gadget); - if (twl->asleep) { - regulator_disable(twl->usb3v3); - twl->asleep = 0; - } + status, twl->xceiv.otg->gadget); + regulator_disable(twl->usb3v3); + twl->asleep = 0; } } + +vbus_notify: + twl->prev_vbus = vbus_state; sysfs_notify(&twl->dev->kobj, NULL, "vbus"); return IRQ_HANDLED; -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html