android_kernel_lenovo_1050f/drivers/usb/dwc3/dwc3-intel-mrfl.c

1377 lines
33 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/gpio.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 <linux/iio/consumer.h>
#include <asm/intel_scu_pmic.h>
#include "otg.h"
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#define VERSION "2.10a"
static int otg_id = -1;
static struct dentry *dwc3_debugfs_root;
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 shady_cove_get_id(struct dwc_otg2 *otg);
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_utmi_phy(struct dwc_otg2 *otg)
{
if (!otg || !otg->otg_data)
return -EINVAL;
return otg->usb2_phy.intf == USB2_PHY_UTMI;
}
void dwc3_switch_mode(struct dwc_otg2 *otg, u32 mode)
{
u32 reg;
reg = otg_read(otg, GCTL);
reg &= ~(GCTL_PRT_CAP_DIR_OTG << GCTL_PRT_CAP_DIR_SHIFT);
reg |= mode << GCTL_PRT_CAP_DIR_SHIFT;
otg_write(otg, GCTL, reg);
}
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)
{
void __iomem *addr;
struct usb_phy *phy;
struct intel_dwc_otg_pdata *data;
if (!otg || !otg->otg_data)
return;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (!phy)
return;
if ((data->usb2_phy_type == USB2_PHY_ULPI) && !!data->ulpi_eye_calibration)
usb_phy_io_write(phy, data->ulpi_eye_calibration, TUSB1211_VENDOR_SPECIFIC1_SET);
else if ((data->usb2_phy_type == USB2_PHY_UTMI) && !!data->utmi_eye_calibration) {
addr = ioremap_nocache(UTMI_PHY_USB2PERPORT, 4);
if (!addr) {
otg_info(otg, "UTMI phy register ioremap failed, use default setup!\n");
usb_put_phy(phy);
return;
}
writel(data->utmi_eye_calibration, addr);
iounmap(addr);
} else
otg_info(otg, "usb2 phy eye optimization fail, use default setup!\n");
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
shady_cove_aca_check(struct dwc_otg2 *otg)
{
if (!otg)
return POWER_SUPPLY_CHARGER_TYPE_NONE;
switch (shady_cove_get_id(otg)) {
case RID_A:
return POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK;
case RID_B:
return POWER_SUPPLY_CHARGER_TYPE_ACA_B;
case RID_C:
return POWER_SUPPLY_CHARGER_TYPE_ACA_C;
default:
return POWER_SUPPLY_CHARGER_TYPE_NONE;
}
}
static enum power_supply_charger_cable_type
dwc3_intel_aca_check(struct dwc_otg2 *otg)
{
if (is_basin_cove(otg))
return basin_cove_aca_check(otg);
else
return shady_cove_aca_check(otg);
}
static int otg_id_show(struct seq_file *s, void *unused)
{
seq_printf(s, "USB OTG ID: %s\n",
(otg_id ? "B" : "A"));
return 0;
}
static int otg_id_open(struct inode *inode, struct file *file)
{
return single_open(file, otg_id_show, inode->i_private);
}
static ssize_t otg_id_write(struct file *file,
const char __user *ubuf, size_t count, loff_t *ppos)
{
char buf[32];
unsigned long flags;
struct dwc_otg2 *otg = dwc3_get_otg();
if (!otg)
return 0;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
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 const struct file_operations otg_debugfs_id_fops = {
.open = otg_id_open,
.write = otg_id_write,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int dwc3_debugfs_init(struct dwc_otg2 *otg)
{
int retval = 0;
struct dentry *file;
if (!dwc3_debugfs_root) {
dwc3_debugfs_root = debugfs_create_dir("dwc3_debugfs_root", usb_debug_root);
if (!dwc3_debugfs_root) {
retval = -ENOMEM;
otg_dbg(otg, " Error create debugfs root failed !");
return retval;
}
file = debugfs_create_file("otg_id", S_IRUGO, dwc3_debugfs_root,
NULL, &otg_debugfs_id_fops);
if (!file) {
retval = -ENOMEM;
otg_dbg(otg, " Error create debugfs file otg_id failed !");
goto remove_debugfs;
}
}
if (retval != 0)
goto remove_debugfs;
return retval;
remove_debugfs:
debugfs_remove_recursive(dwc3_debugfs_root);
dwc3_debugfs_root = NULL;
return retval;
}
static void dwc3_debugfs_exit(struct dwc_otg2 *otg)
{
debugfs_remove_recursive(dwc3_debugfs_root);
dwc3_debugfs_root = NULL;
otg_dbg(otg, " Remove debugfs root !");
}
static void dwc_a_bus_drop(struct usb_phy *x)
{
struct dwc_otg2 *otg = dwc3_get_otg();
unsigned long flags;
spin_lock_irqsave(&otg->lock, flags);
/* When user force exit from battery saver mode,
* Trigger fake ID change event to otg state machine
* to make it transfer to a_host state if current ID is GND.
*/
if (otg->usb2_phy.vbus_state == VBUS_ENABLED)
otg->otg_events |= OEVT_CONN_ID_STS_CHNG_EVNT;
if (otg->usb2_phy.vbus_state == VBUS_DISABLED)
otg->user_events |= USER_A_BUS_DROP;
spin_unlock_irqrestore(&otg->lock, flags);
dwc3_wakeup_otg_thread(otg);
}
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);
}
/* This function is used to control VUSBPHY or assert/deassert USBRST_N
* pin to control usb2 phy enter/exit low power mode.
*/
static int control_usb_phy_power(u16 addr, bool on_off)
{
int ret;
u8 mask, bits;
if (addr == PMIC_VLDOCNT)
mask = PMIC_VLDOCNT_VUSBPHYEN;
else if (addr == PMIC_USBPHYCTRL)
mask = PMIC_USBPHYCTRL_D0;
else
return -EINVAL;
if (on_off)
bits = mask;
else
bits = 0x00;
ret = intel_scu_ipc_update_register(addr,
bits, mask);
/* Debounce 10ms for turn on VUSBPHY */
if (on_off)
usleep_range(10000, 11000);
return ret;
}
/* This function will control VUSBPHY to power gate/ungate USBPHY.
* If current platform haven't using VUSBPHY, then assert/deassert
* USBRST_N pin to make PHY enter reset state.
*/
static int enable_usb_phy(struct dwc_otg2 *otg, bool on_off)
{
struct intel_dwc_otg_pdata *data;
int ret;
if (!otg || !otg->otg_data)
return -EINVAL;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
if (data->using_vusbphy)
ret = control_usb_phy_power(PMIC_VLDOCNT, on_off);
else
ret = control_usb_phy_power(PMIC_USBPHYCTRL, on_off);
if (ret)
otg_err(otg, "dwc3 %s usb phy failed\n",
on_off ? "enable" : "disable");
return ret;
}
static int ulpi_phy_gpio_init(unsigned pin, char *label)
{
int retval = 0;
struct dwc_otg2 *otg = dwc3_get_otg();
otg_dbg(otg, "%s----> %d\n", __func__, pin);
if (gpio_is_valid(pin)) {
retval = gpio_request(pin, label);
if (retval < 0) {
otg_err(otg,
"Request GPIO %d with error %d\n",
pin, retval);
retval = -ENODEV;
goto err;
}
} else {
retval = -ENODEV;
goto err;
}
gpio_direction_input(pin);
otg_dbg(otg, "%s<----\n", __func__);
err:
return retval;
}
int dwc3_intel_platform_init(struct dwc_otg2 *otg)
{
int retval;
int i;
struct intel_dwc_otg_pdata *data;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
/* Init a_bus_drop callback */
otg->usb2_phy.a_bus_drop = dwc_a_bus_drop;
otg->usb2_phy.vbus_state = VBUS_ENABLED;
/* Get usb2 phy type */
otg->usb2_phy.intf = data->usb2_phy_type;
/* Turn off VUSBPHY if it haven't used by USB2 PHY.
* Otherwise, it will consume ~2.6mA(on VSYS) on MOFD.
*/
if (!data->using_vusbphy) {
retval = control_usb_phy_power(PMIC_VLDOCNT, false);
if (retval)
otg_err(otg, "Fail to turn off VUSBPHY\n");
} else if (!is_utmi_phy(otg)) {
/* If the current USB2 PHY low power controlled by VUSBPHY. Then
* we need to de-assert USBRST pin to make USB2 PHY always stay
* in active state.
*/
retval = control_usb_phy_power(PMIC_USBPHYCTRL, true);
if (retval)
otg_err(otg, "Fail to de-assert USBRST#\n");
} else {
struct usb_phy_gp ulpi_phy_gp[ULPI_PHY_GP_NUM] = {
{101, "gp_ulpi_0_clk"},
{136, "gp_ulpi_0_data_0"},
{143, "gp_ulpi_0_data_1"},
{144, "gp_ulpi_0_data_2"},
{145, "gp_ulpi_0_data_3"},
{146, "gp_ulpi_0_data_4"},
{147, "gp_ulpi_0_data_5"},
{148, "gp_ulpi_0_data_6"},
{149, "gp_ulpi_0_data_7"},
{150, "gp_ulpi_0_dir"},
{151, "gp_ulpi_0_nxt"},
{152, "gp_ulpi_0_refclk"},
{153, "gp_ulpi_0_stp"},
};
/* If we are using utmi phy, and through VUSBPHY to do power
* control. Then we need to assert USBRST# for external ULPI phy
* to ask it under inactive state saving power.
*/
retval = control_usb_phy_power(PMIC_USBPHYCTRL, false);
if (retval)
otg_err(otg, "Fail to de-assert USBRST#\n");
/* Optimize unused GPIO power consumption */
for (i = 0; i < ULPI_PHY_GP_NUM; i++) {
retval = ulpi_phy_gpio_init(ulpi_phy_gp[i].num, ulpi_phy_gp[i].label);
if (retval < 0) {
otg_err(otg, "ULPI PHY GPIO init fail\n");
retval = -ENODEV;
break;
}
}
}
/* 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 = dwc3_debugfs_init(otg);
if (retval)
otg_err(otg, "Fail to init the debugfs\n");
otg_dbg(otg, "\n");
otg_write(otg, OEVTEN, 0);
otg_write(otg, OCTL, 0);
dwc3_switch_mode(otg, GCTL_PRT_CAP_DIR_OTG);
return 0;
}
int dwc3_intel_platform_exit(struct dwc_otg2 *otg)
{
dwc3_debugfs_exit(otg);
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);
}
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");
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 shady_cove_get_id(struct dwc_otg2 *otg)
{
u8 schgrirq1;
struct iio_channel *chan;
int ret, rid, id = RID_UNKNOWN;
ret = intel_scu_ipc_ioread8(PMIC_SCHGRIRQ1, &schgrirq1);
if (ret) {
otg_err(otg, "Fail to read id\n");
return id;
}
/* PMIC_SCHGRIRQ1_SUSBIDDET bit definition:
* 0 = RID_A/B/C ; 1 = RID_GND ; 2 = RID_FLOAT */
if (schgrirq1 & PMIC_SCHGRIRQ1_SUSBIDDET(2))
return RID_FLOAT;
else if (schgrirq1 & PMIC_SCHGRIRQ1_SUSBIDDET(1))
return RID_GND;
chan = iio_channel_get(NULL, "USBID");
if (IS_ERR_OR_NULL(chan)) {
otg_err(otg, "%s: Fail to get USBID channel\n", __func__);
return id;
}
ret = iio_read_channel_raw(chan, &rid);
if (ret) {
otg_err(otg, "%s: Fail to read USBID channel", __func__);
goto done;
}
if ((rid > 11150) && (rid < 13640))
id = RID_A;
else if ((rid > 6120) && (rid < 7480))
id = RID_B;
else if ((rid > 3285) && (rid < 4015))
id = RID_C;
done:
iio_channel_release(chan);
return id;
}
int dwc3_intel_get_id(struct dwc_otg2 *otg)
{
if (is_basin_cove(otg))
return basin_cove_get_id(otg);
else
return shady_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);
/* Force config to otg mode as default. */
dwc3_switch_mode(otg, GCTL_PRT_CAP_DIR_OTG);
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_0MA:
ma = 0;
break;
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)
{
atomic_notifier_call_chain(&otg->usb2_phy.notifier,
USB_EVENT_DRIVE_VBUS, &enable);
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) ||
event == POWER_SUPPLY_CHARGER_EVENT_DISCONNECT))
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 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;
if (dwc3_intel_get_id(otg) == RID_GND)
return POWER_SUPPLY_CHARGER_TYPE_B_DEVICE;
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);
if (is_basin_cove(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_GND) {
type = POWER_SUPPLY_CHARGER_TYPE_B_DEVICE;
goto cleanup;
} else 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 isnt 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:
/* WA for EM driver which should not sent VBUS event
* if UTMI PHY selected. */
if (!charger_detect_enable(otg)) {
state = NOTIFY_OK;
goto done;
}
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, "Ignore invalid charger type!\n");
state = NOTIFY_DONE;
goto done;
}
/* Ignore the events which send by USB driver itself. */
if (cap->chrg_evt == POWER_SUPPLY_CHARGER_EVENT_CONNECT)
if (cap_record.chrg_type == POWER_SUPPLY_CHARGER_TYPE_USB_SDP) {
state = NOTIFY_DONE;
goto done;
}
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;
}
if (cap->chrg_type == POWER_SUPPLY_CHARGER_TYPE_ACA_DOCK)
otg->otg_events |= OEVT_CONN_ID_STS_CHNG_EVNT;
state = NOTIFY_OK;
break;
default:
otg_dbg(otg, "DWC OTG Notify unknow notify message\n");
state = NOTIFY_DONE;
}
dwc3_wakeup_otg_thread(otg);
done:
spin_unlock_irqrestore(&otg->lock, flags);
return state;
}
int dwc3_intel_prepare_start_host(struct dwc_otg2 *otg)
{
dwc3_switch_mode(otg, GCTL_PRT_CAP_DIR_HOST);
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)
{
unsigned long flags;
struct intel_dwc_otg_pdata *data;
data = (struct intel_dwc_otg_pdata *)otg->otg_data;
/* Notify 500ma for SDP case whatever HS or SS.
* set_power flow will update 900ma after SS enumeration done. */
if (data && data->charging_compliance &&
(otg->charging_cap.chrg_type ==
POWER_SUPPLY_CHARGER_TYPE_USB_SDP)) {
spin_lock_irqsave(&otg->lock, flags);
otg->charging_cap.ma = 500;
spin_unlock_irqrestore(&otg->lock, flags);
dwc3_intel_notify_charger_type(otg,
POWER_SUPPLY_CHARGER_EVENT_CONNECT);
}
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)
{
int ret;
struct usb_phy *phy;
struct pci_dev *pci_dev;
struct usb_hcd *hcd = NULL;
pci_power_t state = PCI_D3cold;
if (!otg)
return 0;
hcd = container_of(otg->otg.host, struct usb_hcd, self);
pci_dev = to_pci_dev(otg->dev);
if (otg->state == DWC_STATE_A_HOST &&
otg->suspend_host) {
/* Check if USB2 ULPI PHY is hang via access its internal
* registers. If hang, then do hard reset before enter
* hibernation mode. Otherwise, the USB2 PHY can't enter
* suspended state which will blocking U2PMU can't get ready
* then can't enter D0i3hot forever in SCU FW.
*/
if (!is_utmi_phy(otg)) {
phy = usb_get_phy(USB_PHY_TYPE_USB2);
if (!phy)
return -ENODEV;
if (usb_phy_io_read(phy, ULPI_VENDOR_ID_LOW) < 0) {
enable_usb_phy(otg, 0);
enable_usb_phy(otg, 1);
}
usb_put_phy(phy);
}
ret = otg->suspend_host(hcd);
if (ret) {
otg_err(otg, "dwc3-host enter suspend faield: %d\n", ret);
return ret;
}
}
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);
if ((state == PCI_D3cold) && is_utmi_phy(otg)) {
/* Important!! Whenever the VUSBPHY rail is disabled, SW
* must assert USBRST# to isolate the SOCs DP/DM pins from the
* outside world. There is a risk of damage to the SOC if a
* peripheral were to bias DP/DM to 3.3V when the SOC is
* unpowered. */
ret = intel_scu_ipc_update_register(PMIC_USBPHYCTRL,
0x0, USBPHYRSTB);
if (ret)
otg_err(otg, "%s: ipc update failed\n", __func__);
}
pci_set_power_state(pci_dev, state);
return 0;
}
int dwc3_intel_resume(struct dwc_otg2 *otg)
{
struct pci_dev *pci_dev;
struct usb_hcd *hcd = NULL;
u32 data;
int ret;
if (!otg)
return 0;
hcd = container_of(otg->otg.host, struct usb_hcd, self);
/* After resume from D0i3cold. The UTMI PHY D+ drive issue
* reproduced due to all setting be reseted. So switch to OTG
* mode avoid D+ drive too early.
*/
if ((otg->state == DWC_STATE_B_IDLE ||
otg->state == DWC_STATE_CHARGING ||
otg->state == DWC_STATE_WAIT_VBUS_FALL ||
otg->state == DWC_STATE_WAIT_VBUS_RAISE) &&
is_utmi_phy(otg)) {
/* Reconnect DP/DM between Pmic and SOC for support host
* and device mode. */
ret = intel_scu_ipc_update_register(PMIC_USBPHYCTRL,
USBPHYRSTB, USBPHYRSTB);
if (ret)
otg_err(otg, "%s: ipc update failed\n", __func__);
otg_write(otg, OEVTEN, 0);
otg_write(otg, OCTL, 0);
dwc3_switch_mode(otg, GCTL_PRT_CAP_DIR_OTG);
}
/* This is one SCU WA. SCU should set GUSB2PHYCFG0
* bit 4 for ULPI setting. But SCU haven't do that.
* So do WA first until SCU fix.
*/
data = otg_read(otg, GUSB2PHYCFG0);
if (is_utmi_phy(otg))
data &= ~(1 << 4);
else
data |= (1 << 4);
otg_write(otg, GUSB2PHYCFG0, data);
pci_dev = to_pci_dev(otg->dev);
/* From synopsys spec 12.2.11.
* Software cannot access memory-mapped I/O space
* for 10ms. Delay 1 ms here should be enough. Too
* long a delay causes hibernation exit failure
* or high-speed detection handshake failure.
*/
mdelay(1);
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);
if (otg->state == DWC_STATE_A_HOST &&
otg->resume_host)
otg->resume_host(hcd);
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,
.platform_exit = dwc3_intel_platform_exit,
.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);