blob: 058647cfcf3ad91c13b34abd203341009667da7e [file] [log] [blame]
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/otg.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/freezer.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/version.h>
#include <linux/usb.h>
#include <linux/usb/hcd.h>
#include <linux/usb/gadget.h>
#include <linux/usb/dwc3-intel-mid.h>
#include <asm/intel_scu_pmic.h>
#include "otg.h"
#define VERSION "2.10a"
static int otg_id = -1;
static int reboot_flag = 1;
static int enable_usb_phy(struct dwc_otg2 *otg, bool on_off);
static int dwc3_intel_notify_charger_type(struct dwc_otg2 *otg,
enum power_supply_charger_event event);
static struct power_supply_cable_props cap_record;
static int charger_detect_enable(struct dwc_otg2 *otg)
{
struct intel_dwc_otg_pdata *data;
if (!otg || !otg->otg_data)
return 0;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
return data->charger_detect_enable;
}
static int is_basin_cove(struct dwc_otg2 *otg)
{
struct intel_dwc_otg_pdata *data;
if (!otg || !otg->otg_data)
return -EINVAL;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
return data->pmic_type == BASIN_COVE;
}
static int is_hybridvp(struct dwc_otg2 *otg)
{
struct intel_dwc_otg_pdata *data;
if (!otg || !otg->otg_data)
return -EINVAL;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
return data->is_hvp;
}
static void usb2phy_eye_optimization(struct dwc_otg2 *otg)
{
struct usb_phy *phy;
phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (!phy)
return;
/* Set 0x7f for better quality in eye diagram
* It means ZHSDRV = 0b11 and IHSTX = 0b1111*/
usb_phy_io_write(phy, 0x7f, TUSB1211_VENDOR_SPECIFIC1_SET);
usb_put_phy(phy);
}
/* As we use SW mode to do charger detection, need to notify HW
* the result SW get, charging port or not */
static int dwc_otg_charger_hwdet(bool enable)
{
int retval;
struct usb_phy *phy;
struct dwc_otg2 *otg = dwc3_get_otg();
/* Just return if charger detection is not enabled */
if (!charger_detect_enable(otg))
return 0;
phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (!phy)
return -ENODEV;
if (enable) {
retval = usb_phy_io_write(phy, PWCTRL_HWDETECT,
TUSB1211_POWER_CONTROL_SET);
if (retval)
return retval;
otg_dbg(otg, "set HWDETECT\n");
} else {
retval = usb_phy_io_write(phy, PWCTRL_HWDETECT,
TUSB1211_POWER_CONTROL_CLR);
if (retval)
return retval;
otg_dbg(otg, "clear HWDETECT\n");
}
usb_put_phy(phy);
return 0;
}
static enum power_supply_charger_cable_type
basin_cove_aca_check(struct dwc_otg2 *otg)
{
u8 rarbrc;
int ret;
enum power_supply_charger_cable_type type =
POWER_SUPPLY_CHARGER_TYPE_NONE;
ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL,
USBIDCTRL_ACA_DETEN_D1,
USBIDCTRL_ACA_DETEN_D1);
if (ret)
otg_err(otg, "Fail to enable ACA&ID detection logic\n");
/* Wait >66.1ms (for TCHGD_SERX_DEB) */
msleep(66);
/* Read decoded RID value */
ret = intel_scu_ipc_ioread8(PMIC_USBIDSTS, &rarbrc);
if (ret)
otg_err(otg, "Fail to read decoded RID value\n");
rarbrc &= USBIDSTS_ID_RARBRC_STS(3);
rarbrc >>= 1;
/* If ID_RARBRC_STS==01: ACA-Dock detected
* If ID_RARBRC_STS==00: MHL detected
*/
if (rarbrc == 1) {
/* ACA-Dock */
type = POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK;
} else if (!rarbrc) {
/* MHL */
type = POWER_SUPPLY_CHARGER_TYPE_MHL;
}
ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL,
USBIDCTRL_ACA_DETEN_D1,
0);
if (ret)
otg_err(otg, "Fail to enable ACA&ID detection logic\n");
return type;
}
static enum power_supply_charger_cable_type
dwc3_intel_aca_check(struct dwc_otg2 *otg)
{
return basin_cove_aca_check(otg);
}
static ssize_t store_otg_id(struct device *_dev,
struct device_attribute *attr, const char *buf, size_t count)
{
unsigned long flags;
struct dwc_otg2 *otg = dwc3_get_otg();
if (!otg)
return 0;
if (count != 2) {
otg_err(otg, "return EINVAL\n");
return -EINVAL;
}
if (count > 0 && buf[count-1] == '\n')
((char *) buf)[count-1] = 0;
switch (buf[0]) {
case 'a':
case 'A':
otg_dbg(otg, "Change ID to A\n");
otg->user_events |= USER_ID_A_CHANGE_EVENT;
spin_lock_irqsave(&otg->lock, flags);
dwc3_wakeup_otg_thread(otg);
otg_id = 0;
spin_unlock_irqrestore(&otg->lock, flags);
return count;
case 'b':
case 'B':
otg_dbg(otg, "Change ID to B\n");
otg->user_events |= USER_ID_B_CHANGE_EVENT;
spin_lock_irqsave(&otg->lock, flags);
dwc3_wakeup_otg_thread(otg);
otg_id = 1;
spin_unlock_irqrestore(&otg->lock, flags);
return count;
default:
otg_err(otg, "Just support change ID to A!\n");
return -EINVAL;
}
return count;
}
static ssize_t
show_otg_id(struct device *_dev, struct device_attribute *attr, char *buf)
{
char *next;
unsigned size, t;
next = buf;
size = PAGE_SIZE;
t = scnprintf(next, size,
"USB OTG ID: %s\n",
(otg_id ? "B" : "A")
);
size -= t;
next += t;
return PAGE_SIZE - size;
}
static DEVICE_ATTR(otg_id, S_IRUGO|S_IWUSR|S_IWGRP,
show_otg_id, store_otg_id);
static void set_sus_phy(struct dwc_otg2 *otg, int bit)
{
u32 data = 0;
data = otg_read(otg, GUSB2PHYCFG0);
if (bit)
data |= GUSB2PHYCFG_SUS_PHY;
else
data &= ~GUSB2PHYCFG_SUS_PHY;
otg_write(otg, GUSB2PHYCFG0, data);
data = otg_read(otg, GUSB3PIPECTL0);
if (bit)
data |= GUSB3PIPECTL_SUS_EN;
else
data &= ~GUSB3PIPECTL_SUS_EN;
otg_write(otg, GUSB3PIPECTL0, data);
}
int dwc3_intel_platform_init(struct dwc_otg2 *otg)
{
u32 gctl;
int retval;
otg_info(otg, "De-assert USBRST# to enable PHY\n");
retval = intel_scu_ipc_iowrite8(PMIC_USBPHYCTRL,
PMIC_USBPHYCTRL_D0);
if (retval)
otg_err(otg, "Fail to de-assert USBRST#\n");
/* Don't let phy go to suspend mode, which
* will cause FS/LS devices enum failed in host mode.
*/
set_sus_phy(otg, 0);
retval = device_create_file(otg->dev, &dev_attr_otg_id);
if (retval < 0) {
otg_dbg(otg,
"Can't register sysfs attribute: %d\n", retval);
return -ENOMEM;
}
otg_dbg(otg, "\n");
otg_write(otg, OEVTEN, 0);
otg_write(otg, OCTL, 0);
gctl = otg_read(otg, GCTL);
gctl |= GCTL_PRT_CAP_DIR_OTG << GCTL_PRT_CAP_DIR_SHIFT;
otg_write(otg, GCTL, gctl);
return 0;
}
/* Disable auto-resume feature for USB2 PHY. This is one
* silicon workaround. It will cause fabric timeout error
* for LS case after resume from hibernation */
static void disable_phy_auto_resume(struct dwc_otg2 *otg)
{
u32 data = 0;
data = otg_read(otg, GUSB2PHYCFG0);
data &= ~GUSB2PHYCFG_ULPI_AUTO_RESUME;
otg_write(otg, GUSB2PHYCFG0, data);
}
/* This function will control VUSBPHY to power gate/ungate USBPHY */
static int enable_usb_phy(struct dwc_otg2 *otg, bool on_off)
{
int ret;
if (on_off) {
ret = intel_scu_ipc_update_register(PMIC_VLDOCNT,
0xff, PMIC_VLDOCNT_VUSBPHYEN);
if (ret)
otg_err(otg, "Fail to enable VBUSPHY\n");
msleep(20);
} else {
ret = intel_scu_ipc_update_register(PMIC_VLDOCNT,
0x00, PMIC_VLDOCNT_VUSBPHYEN);
if (ret)
otg_err(otg, "Fail to disable VBUSPHY\n");
}
return 0;
}
int basin_cove_get_id(struct dwc_otg2 *otg)
{
int ret, id = RID_UNKNOWN;
u8 idsts, pmic_id;
ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL,
USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0,
USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0);
if (ret)
otg_err(otg, "Fail to enable ACA&ID detection logic\n");
mdelay(50);
ret = intel_scu_ipc_ioread8(PMIC_USBIDSTS, &idsts);
if (ret) {
otg_err(otg, "Fail to read id\n");
return id;
}
if (idsts & USBIDSTS_ID_FLOAT_STS)
id = RID_FLOAT;
else if (idsts & USBIDSTS_ID_RARBRC_STS(1))
id = RID_A;
else if (idsts & USBIDSTS_ID_RARBRC_STS(2))
id = RID_B;
else if (idsts & USBIDSTS_ID_RARBRC_STS(3))
id = RID_C;
else {
/* PMIC A0 reports ID_GND = 0 for RID_GND but PMIC B0 reports
* ID_GND = 1 for RID_GND
*/
ret = intel_scu_ipc_ioread8(0x00, &pmic_id);
if (ret) {
otg_err(otg, "Fail to read PMIC ID register\n");
} else if (((pmic_id & VENDOR_ID_MASK) == BASIN_COVE_PMIC_ID) &&
((pmic_id & PMIC_MAJOR_REV) == PMIC_A0_MAJOR_REV)) {
if (idsts & USBIDSTS_ID_GND)
id = RID_GND;
} else {
if (!(idsts & USBIDSTS_ID_GND))
id = RID_GND;
}
}
ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL,
USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0,
0);
if (ret)
otg_err(otg, "Fail to enable ACA&ID detection logic\n");
return id;
}
int dwc3_intel_get_id(struct dwc_otg2 *otg)
{
return basin_cove_get_id(otg);
}
int dwc3_intel_b_idle(struct dwc_otg2 *otg)
{
u32 gctl, tmp;
/* Disable hibernation mode by default */
gctl = otg_read(otg, GCTL);
gctl &= ~GCTL_GBL_HIBERNATION_EN;
otg_write(otg, GCTL, gctl);
/* Reset ADP related registers */
otg_write(otg, ADPCFG, 0);
otg_write(otg, ADPCTL, 0);
otg_write(otg, ADPEVTEN, 0);
tmp = otg_read(otg, ADPEVT);
otg_write(otg, ADPEVT, tmp);
otg_write(otg, OCFG, 0);
otg_write(otg, OEVTEN, 0);
tmp = otg_read(otg, OEVT);
otg_write(otg, OEVT, tmp);
otg_write(otg, OCTL, OCTL_PERI_MODE);
/* Force config to device mode as default */
gctl = otg_read(otg, GCTL);
gctl &= ~GCTL_PRT_CAP_DIR;
gctl |= GCTL_PRT_CAP_DIR_DEV << GCTL_PRT_CAP_DIR_SHIFT;
otg_write(otg, GCTL, gctl);
if (!is_hybridvp(otg)) {
dwc_otg_charger_hwdet(false);
enable_usb_phy(otg, false);
}
mdelay(100);
return 0;
}
static int dwc3_intel_set_power(struct usb_phy *_otg,
unsigned ma)
{
unsigned long flags;
struct dwc_otg2 *otg = dwc3_get_otg();
struct power_supply_cable_props cap;
struct intel_dwc_otg_pdata *data;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
if (otg->charging_cap.chrg_type ==
POWER_SUPPLY_CHARGER_TYPE_USB_CDP)
return 0;
else if (otg->charging_cap.chrg_type !=
POWER_SUPPLY_CHARGER_TYPE_USB_SDP) {
otg_err(otg, "%s: currently, chrg type is not SDP!\n",
__func__);
return -EINVAL;
}
if (ma == OTG_DEVICE_SUSPEND) {
spin_lock_irqsave(&otg->lock, flags);
cap.chrg_type = otg->charging_cap.chrg_type;
cap.ma = otg->charging_cap.ma;
cap.chrg_evt = POWER_SUPPLY_CHARGER_EVENT_SUSPEND;
spin_unlock_irqrestore(&otg->lock, flags);
/* mA is zero mean D+/D- opened cable.
* If SMIP set, then notify 500mA.
* Otherwise, notify 0mA.
*/
if (!cap.ma) {
if (data->charging_compliance) {
cap.ma = 500;
cap.chrg_evt =
POWER_SUPPLY_CHARGER_EVENT_CONNECT;
}
/* For standard SDP, if SMIP set, then ignore suspend */
} else if (data->charging_compliance)
return 0;
/* Stander SDP(cap.mA != 0) and SMIP not set.
* Should send 0mA with SUSPEND event
*/
else
cap.ma = 0;
atomic_notifier_call_chain(&otg->usb2_phy.notifier,
USB_EVENT_CHARGER, &cap);
otg_dbg(otg, "Notify EM");
otg_dbg(otg, "POWER_SUPPLY_CHARGER_EVENT_SUSPEND\n");
return 0;
} else if (ma == OTG_DEVICE_RESUME) {
otg_dbg(otg, "Notify EM");
otg_dbg(otg, "POWER_SUPPLY_CHARGER_EVENT_CONNECT\n");
dwc3_intel_notify_charger_type(otg,
POWER_SUPPLY_CHARGER_EVENT_CONNECT);
return 0;
}
/* For SMIP set case, only need to report 500/900mA */
if (data->charging_compliance) {
if ((ma != OTG_USB2_500MA) &&
(ma != OTG_USB3_900MA))
return 0;
}
/* Covert macro to integer number*/
switch (ma) {
case OTG_USB2_100MA:
ma = 100;
break;
case OTG_USB3_150MA:
ma = 150;
break;
case OTG_USB2_500MA:
ma = 500;
break;
case OTG_USB3_900MA:
ma = 900;
break;
default:
otg_err(otg, "Device driver set invalid SDP current value!\n");
return -EINVAL;
}
spin_lock_irqsave(&otg->lock, flags);
otg->charging_cap.ma = ma;
spin_unlock_irqrestore(&otg->lock, flags);
dwc3_intel_notify_charger_type(otg,
POWER_SUPPLY_CHARGER_EVENT_CONNECT);
return 0;
}
int dwc3_intel_enable_vbus(struct dwc_otg2 *otg, int enable)
{
if (reboot_flag) {
atomic_notifier_call_chain(&otg->usb2_phy.notifier,
USB_EVENT_VBUS, &enable);
reboot_flag = 0;
}
return 0;
}
static int dwc3_intel_notify_charger_type(struct dwc_otg2 *otg,
enum power_supply_charger_event event)
{
struct power_supply_cable_props cap;
int ret = 0;
unsigned long flags;
if (!charger_detect_enable(otg) &&
(otg->charging_cap.chrg_type !=
POWER_SUPPLY_CHARGER_TYPE_USB_SDP))
return 0;
if (event > POWER_SUPPLY_CHARGER_EVENT_DISCONNECT) {
otg_err(otg,
"%s: Invalid power_supply_charger_event!\n", __func__);
return -EINVAL;
}
if ((otg->charging_cap.chrg_type ==
POWER_SUPPLY_CHARGER_TYPE_USB_SDP) &&
((otg->charging_cap.ma != 0) &&
(otg->charging_cap.ma != 100) &&
(otg->charging_cap.ma != 150) &&
(otg->charging_cap.ma != 500) &&
(otg->charging_cap.ma != 900))) {
otg_err(otg, "%s: invalid SDP current!\n", __func__);
return -EINVAL;
}
spin_lock_irqsave(&otg->lock, flags);
cap.chrg_type = otg->charging_cap.chrg_type;
cap.ma = otg->charging_cap.ma;
cap.chrg_evt = event;
spin_unlock_irqrestore(&otg->lock, flags);
atomic_notifier_call_chain(&otg->usb2_phy.notifier, USB_EVENT_CHARGER,
&cap);
return ret;
}
static void dwc3_phy_soft_reset(struct dwc_otg2 *otg)
{
u32 val;
val = otg_read(otg, GCTL);
val |= GCTL_CORESOFTRESET;
otg_write(otg, GCTL, val);
val = otg_read(otg, GUSB3PIPECTL0);
val |= GUSB3PIPECTL_PHYSOFTRST;
otg_write(otg, GUSB3PIPECTL0, val);
val = otg_read(otg, GUSB2PHYCFG0);
val |= GUSB2PHYCFG_PHYSOFTRST;
otg_write(otg, GUSB2PHYCFG0, val);
msleep(50);
val = otg_read(otg, GUSB3PIPECTL0);
val &= ~GUSB3PIPECTL_PHYSOFTRST;
otg_write(otg, GUSB3PIPECTL0, val);
val = otg_read(otg, GUSB2PHYCFG0);
val &= ~GUSB2PHYCFG_PHYSOFTRST;
otg_write(otg, GUSB2PHYCFG0, val);
msleep(100);
val = otg_read(otg, GCTL);
val &= ~GCTL_CORESOFTRESET;
otg_write(otg, GCTL, val);
}
static enum power_supply_charger_cable_type
dwc3_intel_get_charger_type(struct dwc_otg2 *otg)
{
int ret;
struct usb_phy *phy;
u8 val, vdat_det, chgd_serx_dm;
unsigned long timeout, interval;
enum power_supply_charger_cable_type type =
POWER_SUPPLY_CHARGER_TYPE_NONE;
if (!charger_detect_enable(otg))
return cap_record.chrg_type;
phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (!phy) {
otg_err(otg, "Get USB2 PHY failed\n");
return POWER_SUPPLY_CHARGER_TYPE_NONE;
}
/* PHY Enable:
* Power on PHY
*/
enable_usb_phy(otg, true);
dwc3_phy_soft_reset(otg);
/* Enable ACA:
* Enable ACA & ID detection logic.
*/
ret = intel_scu_ipc_update_register(PMIC_USBIDCTRL,
USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0,
USBIDCTRL_ACA_DETEN_D1 | PMIC_USBPHYCTRL_D0);
if (ret)
otg_err(otg, "Fail to enable ACA&ID detection logic\n");
/* DCD Enable: Change OPMODE to 01 (Non-driving),
* TermSel to 0, &
* XcvrSel to 01 (enable FS xcvr)
*/
usb_phy_io_write(phy, FUNCCTRL_OPMODE(1) | FUNCCTRL_XCVRSELECT(1),
TUSB1211_FUNC_CTRL_SET);
usb_phy_io_write(phy, FUNCCTRL_OPMODE(2) | FUNCCTRL_XCVRSELECT(2)
| FUNCCTRL_TERMSELECT,
TUSB1211_FUNC_CTRL_CLR);
/*Enable SW control*/
usb_phy_io_write(phy, PWCTRL_SW_CONTROL, TUSB1211_POWER_CONTROL_SET);
/* Enable IDPSRC */
usb_phy_io_write(phy, VS3_CHGD_IDP_SRC_EN,
TUSB1211_VENDOR_SPECIFIC3_SET);
/* Check DCD result, use same polling parameter */
timeout = jiffies + msecs_to_jiffies(DATACON_TIMEOUT);
interval = DATACON_INTERVAL * 1000; /* us */
/* DCD Check:
* Delay 66.5 ms. (Note:
* TIDP_SRC_ON + TCHGD_SERX_DEB =
* 347.8us + 66.1ms).
*/
usleep_range(66500, 67000);
while (!time_after(jiffies, timeout)) {
/* Read DP logic level. */
val = usb_phy_io_read(phy, TUSB1211_VENDOR_SPECIFIC4);
if (val < 0) {
otg_err(otg, "ULPI read error! try again\n");
continue;
}
if (!(val & VS4_CHGD_SERX_DP)) {
otg_info(otg, "Data contact detected!\n");
break;
}
/* Polling interval */
usleep_range(interval, interval + 2000);
}
/* Disable DP pullup (Idp_src) */
usb_phy_io_write(phy, VS3_CHGD_IDP_SRC_EN,
TUSB1211_VENDOR_SPECIFIC3_CLR);
/* ID Check:
* Check ID pin state.
*/
val = dwc3_intel_get_id(otg);
if (val != RID_FLOAT) {
type = dwc3_intel_aca_check(otg);
goto cleanup;
}
/* SE1 Det Enable:
* Read DP/DM logic level. Note: use DEBUG
* because VS4 isn’t enabled in this situation.
*/
val = usb_phy_io_read(phy, TUSB1211_DEBUG);
if (val < 0)
otg_err(otg, "ULPI read error!\n");
val &= DEBUG_LINESTATE;
/* If '11': SE1 detected; goto 'Cleanup'.
* Else: goto 'Pri Det Enable'.
*/
if (val == 3) {
type = POWER_SUPPLY_CHARGER_TYPE_SE1;
goto cleanup;
}
/* Pri Det Enable:
* Enable VDPSRC.
*/
usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_SET);
/* Wait >106.1ms (40ms for BC
* Tvdpsrc_on, 66.1ms for TI CHGD_SERX_DEB).
*/
msleep(107);
/* Pri Det Check:
* Check if DM > VDATREF.
*/
vdat_det = usb_phy_io_read(phy, TUSB1211_POWER_CONTROL);
if (vdat_det < 0)
otg_err(otg, "ULPI read error!\n");
vdat_det &= PWCTRL_VDAT_DET;
/* Check if DM<VLGC */
chgd_serx_dm = usb_phy_io_read(phy, TUSB1211_VENDOR_SPECIFIC4);
if (chgd_serx_dm < 0)
otg_err(otg, "ULPI read error!\n");
chgd_serx_dm &= VS4_CHGD_SERX_DM;
/* If VDAT_DET==0 || CHGD_SERX_DM==1: SDP detected
* If VDAT_DET==1 && CHGD_SERX_DM==0: CDP/DCP
*/
if (vdat_det == 0 || chgd_serx_dm == 1)
type = POWER_SUPPLY_CHARGER_TYPE_USB_SDP;
/* Disable VDPSRC. */
usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_CLR);
/* If SDP, goto “Cleanup”.
* Else, goto “Sec Det Enable”
*/
if (type == POWER_SUPPLY_CHARGER_TYPE_USB_SDP)
goto cleanup;
/* Sec Det Enable:
* delay 1ms.
*/
usleep_range(1000, 1500);
/* Swap DP & DM */
usb_phy_io_write(phy, VS1_DATAPOLARITY, TUSB1211_VENDOR_SPECIFIC1_CLR);
/* Enable 'VDMSRC'. */
usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_SET);
/* Wait >73ms (40ms for BC Tvdmsrc_on, 33ms for TI TVDPSRC_DEB) */
msleep(80);
/* Sec Det Check:
* Check if DP>VDATREF.
*/
val = usb_phy_io_read(phy, TUSB1211_POWER_CONTROL);
if (val < 0)
otg_err(otg, "ULPI read error!\n");
val &= PWCTRL_VDAT_DET;
/* If VDAT_DET==0: CDP detected.
* If VDAT_DET==1: DCP detected.
*/
if (!val)
type = POWER_SUPPLY_CHARGER_TYPE_USB_CDP;
else
type = POWER_SUPPLY_CHARGER_TYPE_USB_DCP;
/* Disable VDMSRC. */
usb_phy_io_write(phy, PWCTRL_DP_VSRC_EN, TUSB1211_POWER_CONTROL_CLR);
/* Swap DP & DM. */
usb_phy_io_write(phy, VS1_DATAPOLARITY, TUSB1211_VENDOR_SPECIFIC1_SET);
cleanup:
/* If DCP detected, assert VDPSRC. */
if (type == POWER_SUPPLY_CHARGER_TYPE_USB_DCP)
usb_phy_io_write(phy, PWCTRL_SW_CONTROL | PWCTRL_DP_VSRC_EN,
TUSB1211_POWER_CONTROL_SET);
usb_put_phy(phy);
switch (type) {
case POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK:
case POWER_SUPPLY_CHARGER_TYPE_ACA_A:
case POWER_SUPPLY_CHARGER_TYPE_ACA_B:
case POWER_SUPPLY_CHARGER_TYPE_ACA_C:
case POWER_SUPPLY_CHARGER_TYPE_USB_DCP:
case POWER_SUPPLY_CHARGER_TYPE_USB_CDP:
case POWER_SUPPLY_CHARGER_TYPE_SE1:
dwc_otg_charger_hwdet(true);
break;
default:
break;
};
return type;
}
static int dwc3_intel_handle_notification(struct notifier_block *nb,
unsigned long event, void *data)
{
int state;
unsigned long flags, valid_chrg_type;
struct dwc_otg2 *otg = dwc3_get_otg();
struct power_supply_cable_props *cap;
if (!otg)
return NOTIFY_BAD;
valid_chrg_type = POWER_SUPPLY_CHARGER_TYPE_USB_SDP |
POWER_SUPPLY_CHARGER_TYPE_USB_CDP |
POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK;
spin_lock_irqsave(&otg->lock, flags);
switch (event) {
case USB_EVENT_ID:
otg->otg_events |= OEVT_CONN_ID_STS_CHNG_EVNT;
state = NOTIFY_OK;
break;
case USB_EVENT_VBUS:
if (*(int *)data) {
otg->otg_events |= OEVT_B_DEV_SES_VLD_DET_EVNT;
otg->otg_events &= ~OEVT_A_DEV_SESS_END_DET_EVNT;
} else {
otg->otg_events |= OEVT_A_DEV_SESS_END_DET_EVNT;
otg->otg_events &= ~OEVT_B_DEV_SES_VLD_DET_EVNT;
}
state = NOTIFY_OK;
break;
case USB_EVENT_CHARGER:
if (charger_detect_enable(otg)) {
state = NOTIFY_DONE;
goto done;
}
cap = (struct power_supply_cable_props *)data;
if (!(cap->chrg_type & valid_chrg_type)) {
otg_err(otg, "Invalid charger type!\n");
state = NOTIFY_BAD;
}
if (cap->chrg_evt == POWER_SUPPLY_CHARGER_EVENT_CONNECT) {
otg->otg_events |= OEVT_B_DEV_SES_VLD_DET_EVNT;
otg->otg_events &= ~OEVT_A_DEV_SESS_END_DET_EVNT;
cap_record.chrg_type = cap->chrg_type;
cap_record.ma = cap->ma;
cap_record.chrg_evt = cap->chrg_evt;
} else if (cap->chrg_evt ==
POWER_SUPPLY_CHARGER_EVENT_DISCONNECT) {
otg->otg_events |= OEVT_A_DEV_SESS_END_DET_EVNT;
otg->otg_events &= ~OEVT_B_DEV_SES_VLD_DET_EVNT;
cap_record.chrg_type = POWER_SUPPLY_CHARGER_TYPE_NONE;
cap_record.ma = 0;
cap_record.chrg_evt =
POWER_SUPPLY_CHARGER_EVENT_DISCONNECT;
}
state = NOTIFY_OK;
break;
default:
otg_dbg(otg, "DWC OTG Notify unknow notify message\n");
state = NOTIFY_DONE;
}
done:
dwc3_wakeup_otg_thread(otg);
spin_unlock_irqrestore(&otg->lock, flags);
return state;
}
int dwc3_intel_prepare_start_host(struct dwc_otg2 *otg)
{
if (!is_hybridvp(otg)) {
enable_usb_phy(otg, true);
usb2phy_eye_optimization(otg);
disable_phy_auto_resume(otg);
}
return 0;
}
int dwc3_intel_prepare_start_peripheral(struct dwc_otg2 *otg)
{
if (!is_hybridvp(otg)) {
enable_usb_phy(otg, true);
usb2phy_eye_optimization(otg);
disable_phy_auto_resume(otg);
}
return 0;
}
int dwc3_intel_suspend(struct dwc_otg2 *otg)
{
struct pci_dev *pci_dev;
pci_power_t state = PCI_D3cold;
if (!otg)
return 0;
pci_dev = to_pci_dev(otg->dev);
if (otg->state == DWC_STATE_B_PERIPHERAL ||
otg->state == DWC_STATE_A_HOST)
state = PCI_D3hot;
set_sus_phy(otg, 1);
if (pci_save_state(pci_dev)) {
otg_err(otg, "pci_save_state failed!\n");
return -EIO;
}
pci_disable_device(pci_dev);
pci_set_power_state(pci_dev, state);
return 0;
}
int dwc3_intel_resume(struct dwc_otg2 *otg)
{
struct pci_dev *pci_dev;
if (!otg)
return 0;
pci_dev = to_pci_dev(otg->dev);
/* From synopsys spec 12.2.11.
* Software cannot access memory-mapped I/O space
* for 10ms. Delay 5 ms here should be enough. Too
* long a delay causes hibernation exit failure.
*/
mdelay(5);
pci_restore_state(pci_dev);
if (pci_enable_device(pci_dev) < 0) {
otg_err(otg, "pci_enable_device failed.\n");
return -EIO;
}
set_sus_phy(otg, 0);
/* Delay 1ms waiting PHY clock debounce.
* Without this debounce, will met fabric error randomly.
**/
mdelay(1);
return 0;
}
struct dwc3_otg_hw_ops dwc3_intel_otg_pdata = {
.mode = DWC3_DRD,
.bus = DWC3_PCI,
.get_id = dwc3_intel_get_id,
.b_idle = dwc3_intel_b_idle,
.set_power = dwc3_intel_set_power,
.enable_vbus = dwc3_intel_enable_vbus,
.platform_init = dwc3_intel_platform_init,
.get_charger_type = dwc3_intel_get_charger_type,
.otg_notifier_handler = dwc3_intel_handle_notification,
.prepare_start_peripheral = dwc3_intel_prepare_start_peripheral,
.prepare_start_host = dwc3_intel_prepare_start_host,
.notify_charger_type = dwc3_intel_notify_charger_type,
.suspend = dwc3_intel_suspend,
.resume = dwc3_intel_resume,
};
static int __init dwc3_intel_init(void)
{
return dwc3_otg_register(&dwc3_intel_otg_pdata);
}
module_init(dwc3_intel_init);
static void __exit dwc3_intel_exit(void)
{
dwc3_otg_unregister(&dwc3_intel_otg_pdata);
}
module_exit(dwc3_intel_exit);
MODULE_AUTHOR("Wang Yu <yu.y.wang@intel.com>");
MODULE_DESCRIPTION("DWC3 Intel OTG Driver");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(VERSION);