drivers: usb: otg: Report the charger type and enable the USB host mode according to the ID pin status and the SlimPort status.

Change-Id: Iedc793de11c53663c53cc9ad4e5c2d6866aff0fe
Change-Id: Iaf4db6d029a728b2ae6b3fc9afa09c12521deb18
Reviewed-on: http://mcrd1-5.corpnet.asus/code-review/master/68181
Reviewed-by: Yi-Hsin Hung <Yi-Hsin_Hung@asus.com>
Tested-by: Yi-Hsin Hung <Yi-Hsin_Hung@asus.com>
Reviewed-by: Leslie Yu <Leslie_Yu@asus.com>
This commit is contained in:
yi-hsin_hung 2013-02-01 15:10:07 +08:00 committed by Iliyan Malchev
parent c822119776
commit 920fbc333a
4 changed files with 165 additions and 5 deletions

View file

@ -72,6 +72,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_POWER_SUPPLY) += power/
obj-$(CONFIG_USB_OTG_UTILS) += usb/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_PCI) += usb/
@ -85,7 +86,6 @@ obj-y += i2c/ media/
obj-$(CONFIG_PPS) += pps/
obj-$(CONFIG_PTP_1588_CLOCK) += ptp/
obj-$(CONFIG_W1) += w1/
obj-$(CONFIG_POWER_SUPPLY) += power/
obj-$(CONFIG_HWMON) += hwmon/
obj-$(CONFIG_THERMAL) += thermal/
obj-$(CONFIG_WATCHDOG) += watchdog/

View file

@ -50,6 +50,8 @@ static bool hdcp_enable = 1;
static bool hdcp_enable = 0;
#endif
extern void msm_otg_id_pin_irq_enabled(bool enabled);
//extern void msm_otg_id_pin_irq_enabled(bool enabled);
int sp_read_reg(uint8_t slave_addr, uint8_t offset, uint8_t *buf)
@ -383,14 +385,14 @@ static irqreturn_t anx7808_cbl_det_isr(int irq, void *data)
wake_lock(&anx7808->slimport_lock);
SP_DEV_DBG("%s : detect cable insertion\n", __func__);
queue_delayed_work(anx7808->workqueue, &anx7808->work, 0);
//msm_otg_id_pin_irq_enabled(false);
msm_otg_id_pin_irq_enabled(false);
} else {
SP_DEV_DBG("%s : detect cable removal\n", __func__);
status = cancel_delayed_work_sync(&anx7808->work);
if(status == 0)
flush_workqueue(anx7808 ->workqueue);
wake_unlock(&anx7808->slimport_lock);
//msm_otg_id_pin_irq_enabled(true);
msm_otg_id_pin_irq_enabled(true);
}
return IRQ_HANDLED;
}

161
drivers/usb/otg/msm_otg.c Normal file → Executable file
View file

@ -88,11 +88,25 @@ static struct power_supply *psy;
static bool aca_id_turned_on;
static struct workqueue_struct *msm_otg_acok_wq;
static struct workqueue_struct *msm_otg_id_pin_wq;
static int pm_suspend_acok_status = 0;
/* APQ8064 GPIO pin definition */
#define APQ_AP_ACOK 23
#define APQ_OTG_ID_PIN 77
enum msm_otg_smb345_chg_type {
CHARGER_NONE = 0,
CHARGER_CDP,
CHARGER_DCP,
CHARGER_OTHER,
CHARGER_SDP,
CHARGER_ACA,
CHARGER_TBD,
};
extern int usb_cable_type_detect(unsigned int chgr_type);
extern void smb345_otg_status(bool on);
static inline bool aca_enabled(void)
{
#ifdef CONFIG_USB_MSM_ACA
@ -115,6 +129,44 @@ static const int vdd_val[VDD_TYPE_MAX][VDD_VAL_MAX] = {
},
};
static void asus_chg_set_chg_mode(enum usb_chg_type chg_src)
{
int chg_type = chg_src;
switch (chg_type) {
case USB_INVALID_CHARGER:
usb_cable_type_detect(CHARGER_NONE);
printk(KERN_INFO "The USB cable status = CHARGER_NONE\n");
break;
case USB_SDP_CHARGER:
usb_cable_type_detect(CHARGER_SDP);
printk(KERN_INFO "The USB cable status = CHARGER_SDP\n");
break;
case USB_DCP_CHARGER:
usb_cable_type_detect(CHARGER_DCP);
printk(KERN_INFO "The USB cable status = CHARGER_DCP\n");
break;
case USB_CDP_CHARGER:
usb_cable_type_detect(CHARGER_CDP);
printk(KERN_INFO "The USB cable status = CHARGER_CDP\n");
break;
case USB_ACA_A_CHARGER:
case USB_ACA_B_CHARGER:
case USB_ACA_C_CHARGER:
case USB_ACA_DOCK_CHARGER:
usb_cable_type_detect(CHARGER_ACA);
printk(KERN_INFO "The USB cable status = CHARGER_ACA\n");
break;
case USB_PROPRIETARY_CHARGER:
usb_cable_type_detect(CHARGER_OTHER);
printk(KERN_INFO "The USB cable status = CHARGER_OTHER\n");
break;
default:
usb_cable_type_detect(CHARGER_TBD);
printk(KERN_INFO "The USB cable status = CHARGER_TBD\n");
}
}
static int msm_hsusb_ldo_init(struct msm_otg *motg, int init)
{
int rc = 0;
@ -1182,6 +1234,7 @@ static int msm_otg_set_power(struct usb_phy *phy, unsigned mA)
return 0;
}
static int otg_host_on = 0;
static void msm_otg_start_host(struct usb_otg *otg, int on)
{
struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
@ -1195,6 +1248,8 @@ static void msm_otg_start_host(struct usb_otg *otg, int on)
if (on) {
dev_dbg(otg->phy->dev, "host on\n");
smb345_otg_status(true);
otg_host_on = 1;
if (pdata->otg_control == OTG_PHY_CONTROL)
ulpi_write(otg->phy, OTG_COMP_DISABLE,
@ -1221,6 +1276,9 @@ static void msm_otg_start_host(struct usb_otg *otg, int on)
if (pdata->otg_control == OTG_PHY_CONTROL)
ulpi_write(otg->phy, OTG_COMP_DISABLE,
ULPI_CLR(ULPI_PWR_CLK_MNG_REG));
smb345_otg_status(false);
otg_host_on = 0;
}
}
@ -2100,7 +2158,7 @@ static void msm_chg_detect_work(struct work_struct *w)
msm_chg_block_off(motg);
motg->chg_state = USB_CHG_STATE_DETECTED;
motg->chg_type = USB_SDP_CHARGER;
queue_work(system_nrt_wq, &motg->sm_work);
queue_delayed_work(system_nrt_wq, &motg->chg_work, 0);
return;
}
if (msm_chg_mhl_detect(motg)) {
@ -2194,6 +2252,7 @@ static void msm_chg_detect_work(struct work_struct *w)
msm_chg_enable_aca_intr(motg);
dev_info(phy->dev, "chg_type = %s\n",
chg_to_string(motg->chg_type));
asus_chg_set_chg_mode(motg->chg_type);
queue_work(system_nrt_wq, &motg->sm_work);
return;
default:
@ -2395,6 +2454,7 @@ static void msm_otg_sm_work(struct work_struct *w)
cancel_delayed_work_sync(&motg->check_ta_work);
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
asus_chg_set_chg_mode(USB_INVALID_CHARGER);
msm_otg_notify_charger(motg, 0);
msm_otg_reset(otg->phy);
/*
@ -3068,6 +3128,31 @@ static void acok_irq_work_function(struct work_struct *work)
}
}
static void id_pin_irq_work_function(struct work_struct *work)
{
struct msm_otg *motg = container_of(work, struct msm_otg, id_pin_irq_work.work);
struct usb_phy *phy = &motg->phy;
int gpio = gpio_get_value(APQ_OTG_ID_PIN);
if (slimport_is_connected())
pr_info("%s: slimport_is_connected\n", __func__);
else {
if (gpio == 0) {
pr_info("%s: APQ_OTG_ID_PIN is low : Host mode\n", __func__);
set_bit(A_BUS_REQ, &motg->inputs);
clear_bit(ID, &motg->inputs);
pm_runtime_resume(phy->dev);
queue_work(system_nrt_wq, &motg->sm_work);
} else if (otg_host_on == 1 && gpio == 1){
pr_info("%s: APQ_OTG_ID_PIN is high : leave Host mode\n", __func__);
set_bit(ID, &motg->inputs);
clear_bit(B_SESS_VLD, &motg->inputs);
pm_runtime_resume(phy->dev);
queue_work(system_nrt_wq, &motg->sm_work);
}
}
}
static int msm_otg_register_vbus_sn(void (*callback)(int))
{
pr_debug("%p\n", callback);
@ -3090,6 +3175,14 @@ static irqreturn_t msm_otg_acok_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
static irqreturn_t msm_otg_id_pin_irq(int irq, void *dev_id)
{
struct msm_otg *motg = dev_id;
queue_delayed_work(msm_otg_acok_wq, &motg->id_pin_irq_work, 0.6*HZ);
return IRQ_HANDLED;
}
static void msm_otg_acok_init(struct msm_otg *motg)
{
int err = 0 ;
@ -3125,6 +3218,60 @@ static void msm_otg_acok_free(struct msm_otg *motg)
free_irq(irq_num, motg);
}
static int id_pin_irq_enable = 0;
static void msm_otg_id_pin_init(struct msm_otg *motg)
{
int err = 0 ;
unsigned gpio = APQ_OTG_ID_PIN;
unsigned irq_num = gpio_to_irq(gpio);
err = gpio_request(gpio, "msm_otg_id_pin");
if (err) {
printk("gpio %d request failed \n", gpio);
}
err = gpio_direction_input(gpio);
if (err) {
printk("gpio %d unavaliable for input \n", gpio);
}
err = request_irq(irq_num, msm_otg_id_pin_irq, IRQF_TRIGGER_FALLING |IRQF_TRIGGER_RISING|IRQF_SHARED,
"msm_otg_id_pin", motg);
if (err < 0) {
printk("%s irq %d request failed \n","msm_otg_id_pin", irq_num);
}
printk("%s: GPIO pin irq %d requested ok, msm_otg_id_pin = %s\n", __func__, irq_num, gpio_get_value(gpio)? "H":"L");
enable_irq_wake(irq_num);
id_pin_irq_enable = 1;
}
static void msm_otg_id_pin_free(struct msm_otg *motg)
{
unsigned gpio = APQ_OTG_ID_PIN;
unsigned irq_num = gpio_to_irq(gpio);
disable_irq_wake(irq_num);
free_irq(irq_num, motg);
}
void msm_otg_id_pin_irq_enabled(bool enabled)
{
unsigned irq_num = gpio_to_irq(APQ_OTG_ID_PIN);
if (enabled && id_pin_irq_enable == 0) {
enable_irq(irq_num);
enable_irq_wake(irq_num);
id_pin_irq_enable = 1;
} else if (!enabled && id_pin_irq_enable == 1) {
disable_irq_wake(irq_num);
disable_irq(irq_num);
id_pin_irq_enable = 0;
}
pr_info("%s : id_pin_irq_enable = %d\n", __func__, id_pin_irq_enable);
}
EXPORT_SYMBOL( msm_otg_id_pin_irq_enabled);
static void msm_pmic_id_status_w(struct work_struct *w)
{
struct msm_otg *motg = container_of(w, struct msm_otg,
@ -3867,6 +4014,10 @@ static int __init msm_otg_probe(struct platform_device *pdev)
msm_otg_acok_wq = create_singlethread_workqueue("msm_otg_acok_wq");
INIT_DELAYED_WORK_DEFERRABLE(&motg->acok_irq_work, acok_irq_work_function);
msm_otg_id_pin_init(motg);
msm_otg_id_pin_wq = create_singlethread_workqueue("msm_otg_id_pin_wq");
INIT_DELAYED_WORK_DEFERRABLE(&motg->id_pin_irq_work, id_pin_irq_work_function);
ret = msm_otg_debugfs_init(motg);
if (ret)
dev_dbg(&pdev->dev, "mode debugfs file is"
@ -3903,6 +4054,7 @@ static int __init msm_otg_probe(struct platform_device *pdev)
}
queue_delayed_work(msm_otg_acok_wq, &motg->acok_irq_work, 0.5*HZ);
queue_delayed_work(msm_otg_id_pin_wq, &motg->id_pin_irq_work, 0.5*HZ);
return 0;
@ -3974,6 +4126,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
wake_lock_destroy(&motg->wlock);
msm_otg_id_pin_free(motg);
msm_otg_acok_free(motg);
msm_hsusb_mhl_switch_enable(motg, 0);
@ -4084,7 +4237,7 @@ static int msm_otg_pm_resume(struct device *dev)
{
int ret = 0;
struct msm_otg *motg = dev_get_drvdata(dev);
int ac_gpio;
int ac_gpio, id_pin_gpio;
dev_dbg(dev, "OTG PM resume\n");
@ -4108,6 +4261,10 @@ static int msm_otg_pm_resume(struct device *dev)
if (pm_suspend_acok_status != ac_gpio)
acok_irq_work_function(NULL);
id_pin_gpio = gpio_get_value(APQ_OTG_ID_PIN);
if (otg_host_on == id_pin_gpio)
queue_delayed_work(msm_otg_id_pin_wq, &motg->id_pin_irq_work, 0);
return ret;
}
#endif

View file

@ -370,6 +370,7 @@ struct msm_otg {
struct hrtimer timer;
enum usb_vdd_type vdd_type;
struct delayed_work acok_irq_work;
struct delayed_work id_pin_irq_work;
};
struct msm_hsic_host_platform_data {