android_kernel_samsung_hero.../drivers/usb/dwc3/dwc3-sec.c
2016-08-17 16:41:52 +08:00

217 lines
5.3 KiB
C

/*
* Copyright (C) 2014 Samsung Electronics Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/usb_notify.h>
#include <linux/of_gpio.h>
#ifdef CONFIG_EXTCON
#include <linux/extcon.h>
#include <linux/power_supply.h>
#endif
struct dwc3_msm;
struct dwc3_sec {
struct notifier_block nb;
struct dwc3_msm *dwcm;
};
static struct dwc3_sec sec_noti;
void sec_usb_work(int usb_mode)
{
struct power_supply *psy;
#if defined(CONFIG_BATTERY_SAMSUNG)
psy = power_supply_get_by_name("dwc-usb");
#else
psy = power_supply_get_by_name("usb");
#endif
pr_info("usb: dwc3 power supply set(%d)", usb_mode);
if (psy)
power_supply_set_present(psy, usb_mode);
else
pr_err("usb: dwc-usb power supply is null!\n");
}
EXPORT_SYMBOL(sec_usb_work);
static void dwc3_resume_work(struct work_struct *w);
static int sec_otg_ext_notify(struct dwc3_msm *mdwc, int enable)
{
struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
mdwc->ext_xceiv.id = enable ? DWC3_ID_GROUND : DWC3_ID_FLOAT;
if (atomic_read(&dwc->in_lpm)) {
dev_info(mdwc->dev, "%s: calling resume_work\n", __func__);
dwc3_resume_work(&mdwc->resume_work.work);
} else {
dev_info(mdwc->dev, "%s: notifying xceiv event\n", __func__);
if (mdwc->otg_xceiv)
mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg);
}
return 0;
}
int sec_handle_event(bool enable)
{
struct dwc3_sec *snoti = &sec_noti;
struct dwc3_msm *dwcm;
pr_info("%s: event %d\n", __func__, enable);
if (!snoti) {
pr_err("%s: dwc3_otg (snoti) is null\n", __func__);
return NOTIFY_BAD;
}
dwcm = snoti->dwcm;
if (!dwcm) {
pr_err("%s: dwc3_otg (dwcm) is null\n", __func__);
return NOTIFY_BAD;
}
if (enable) {
pr_info("USB OTG START : ID clear\n");
sec_otg_ext_notify(dwcm, 1);
} else {
pr_info("USB OTG STOP : ID set\n");
sec_otg_ext_notify(dwcm, 0);
}
return 0;
}
EXPORT_SYMBOL(sec_handle_event);
#ifdef CONFIG_EXTCON
struct sec_cable {
struct work_struct work;
struct notifier_block nb;
struct extcon_specific_cable_nb extcon_nb;
struct extcon_dev *edev;
enum extcon_cable_name cable_type;
int cable_state;
};
static struct sec_cable support_cable_list[] = {
{ .cable_type = EXTCON_USB, },
{ .cable_type = EXTCON_USB_HOST, },
{ .cable_type = EXTCON_USB_HOST_5V, },
{ .cable_type = EXTCON_TA, },
{ .cable_type = EXTCON_AUDIODOCK, },
{ .cable_type = EXTCON_SMARTDOCK_TA, },
{ .cable_type = EXTCON_SMARTDOCK_USB, },
{ .cable_type = EXTCON_JIG_USBON, },
{ .cable_type = EXTCON_CHARGE_DOWNSTREAM, },
};
static void sec_cable_event_worker(struct work_struct *work)
{
struct sec_cable *cable =
container_of(work, struct sec_cable, work);
struct otg_notify *n = get_otg_notify();
pr_info("%s: %s(%d) is %s\n", __func__,
extcon_cable_name[cable->cable_type],
cable->cable_type,
cable->cable_state ? "attached" : "detached");
switch (cable->cable_type) {
case EXTCON_USB:
case EXTCON_SMARTDOCK_USB:
case EXTCON_JIG_USBON:
case EXTCON_CHARGE_DOWNSTREAM:
if (cable->cable_state)
send_otg_notify(n, NOTIFY_EVENT_VBUS, 1);
else
send_otg_notify(n, NOTIFY_EVENT_VBUS, 0);
break;
case EXTCON_USB_HOST:
if (cable->cable_state) {
send_otg_notify(n, NOTIFY_EVENT_DRIVE_VBUS, 1);
send_otg_notify(n, NOTIFY_EVENT_HOST, 1);
} else {
send_otg_notify(n, NOTIFY_EVENT_HOST, 0);
send_otg_notify(n, NOTIFY_EVENT_DRIVE_VBUS, 0);
}
break;
case EXTCON_AUDIODOCK:
if (cable->cable_state)
send_otg_notify(n, NOTIFY_EVENT_AUDIODOCK, 1);
else
send_otg_notify(n, NOTIFY_EVENT_AUDIODOCK, 0);
break;
case EXTCON_SMARTDOCK_TA:
if (cable->cable_state)
send_otg_notify(n, NOTIFY_EVENT_SMARTDOCK_TA, 1);
else
send_otg_notify(n, NOTIFY_EVENT_SMARTDOCK_TA, 0);
break;
case EXTCON_USB_HOST_5V:
if (cable->cable_state)
send_otg_notify(n, NOTIFY_EVENT_VBUSPOWER, 1);
else
send_otg_notify(n, NOTIFY_EVENT_VBUSPOWER, 0);
default:
break;
}
}
static int sec_cable_notifier(struct notifier_block *nb,
unsigned long stat, void *ptr)
{
struct sec_cable *cable =
container_of(nb, struct sec_cable, nb);
cable->cable_state = stat;
pr_info("sec_cable_notifier: state %lu\n", stat);
queue_work(system_nrt_wq, &cable->work);
return NOTIFY_DONE;
}
static int __init sec_otg_init_cable_notify(void)
{
struct sec_cable *cable;
int i;
int ret;
pr_info("%s register extcon notifier for usb and ta\n", __func__);
for (i = 0; i < ARRAY_SIZE(support_cable_list); i++) {
cable = &support_cable_list[i];
INIT_WORK(&cable->work, sec_cable_event_worker);
cable->nb.notifier_call = sec_cable_notifier;
ret = extcon_register_interest(&cable->extcon_nb,
EXTCON_DEV_NAME,
extcon_cable_name[cable->cable_type],
&cable->nb);
if (ret)
pr_err("%s: fail to register extcon notifier(%s, %d)\n",
__func__, extcon_cable_name[cable->cable_type],
ret);
cable->edev = cable->extcon_nb.edev;
if (!cable->edev)
pr_err("%s: fail to get extcon device\n", __func__);
}
return 0;
}
device_initcall_sync(sec_otg_init_cable_notify);
#endif
static int sec_otg_init(struct dwc3_msm *dwcm, struct usb_phy *phy)
{
int ret = 0;
pr_info("%s: register notifier\n", __func__);
sec_noti.dwcm = dwcm;
return ret;
}