Merge remote-tracking branch 'origin/usb/phy' into usb/next

* origin/usb/phy: (14 commits)
  Doc: ABI: add usb charger uevent
  usb: phy: show USB charger type for user
  MLK-19850-1 usb: phy: mxs: add DCD implementation
  MLK-16576 usb: phy: mxs: set hold_ring_off for USB2 PLL power up
  MLK-14947-2 usb: phy: add mxs phy driver dependency for ARM64
  ...
This commit is contained in:
Dong Aisheng 2019-12-02 18:02:02 +08:00
commit b4e4776fd6
5 changed files with 473 additions and 32 deletions

View File

@ -0,0 +1,45 @@
What: Raise a uevent when a USB charger is inserted or removed
Date: 2019-11-11
KernelVersion: 5.5
Contact: linux-usb@vger.kernel.org
Description: There are two USB charger states:
USB_CHARGER_ABSENT
USB_CHARGER_PRESENT
There are five USB charger types:
USB_CHARGER_UNKNOWN_TYPE
USB_CHARGER_SDP_TYPE
USB_CHARGER_CDP_TYPE
USB_CHARGER_DCP_TYPE
USB_CHARGER_ACA_TYPE
Here are two examples taken using udevadm monitor -p when
USB charger is online:
UDEV [227.425096] change /devices/soc0/usbphynop1 (platform)
ACTION=change
DEVPATH=/devices/soc0/usbphynop1
DRIVER=usb_phy_generic
MODALIAS=of:Nusbphynop1T(null)Cusb-nop-xceiv
OF_COMPATIBLE_0=usb-nop-xceiv
OF_COMPATIBLE_N=1
OF_FULLNAME=/usbphynop1
OF_NAME=usbphynop1
SEQNUM=2493
SUBSYSTEM=platform
USB_CHARGER_STATE=USB_CHARGER_PRESENT
USB_CHARGER_TYPE=USB_CHARGER_SDP_TYPE
USEC_INITIALIZED=227422826
USB charger is offline:
KERNEL[229.533933] change /devices/soc0/usbphynop1 (platform)
ACTION=change
DEVPATH=/devices/soc0/usbphynop1
DRIVER=usb_phy_generic
MODALIAS=of:Nusbphynop1T(null)Cusb-nop-xceiv
OF_COMPATIBLE_0=usb-nop-xceiv
OF_COMPATIBLE_N=1
OF_FULLNAME=/usbphynop1
OF_NAME=usbphynop1
SEQNUM=2494
SUBSYSTEM=platform
USB_CHARGER_STATE=USB_CHARGER_ABSENT
USB_CHARGER_TYPE=USB_CHARGER_UNKNOWN_TYPE

View File

@ -152,7 +152,7 @@ config USB_MV_OTG
config USB_MXS_PHY
tristate "Freescale MXS USB PHY support"
depends on ARCH_MXC || ARCH_MXS
depends on ARCH_MXC || ARCH_MXS || ARCH_MXC_ARM64
select STMP_DEVICE
select USB_PHY
help

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2012-2014 Freescale Semiconductor, Inc.
* Copyright 2012-2016 Freescale Semiconductor, Inc.
* Copyright 2017 NXP
* Copyright (C) 2012 Marek Vasut <marex@denx.de>
* on behalf of DENX Software Engineering GmbH
*/
@ -18,6 +19,7 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/iopoll.h>
#include <linux/regulator/consumer.h>
#define DRIVER_NAME "mxs_phy"
@ -70,6 +72,12 @@
#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6)
/* Anatop Registers */
#define ANADIG_PLL_USB2 0x20
#define ANADIG_PLL_USB2_SET 0x24
#define ANADIG_PLL_USB2_CLR 0x28
#define ANADIG_REG_1P1_SET 0x114
#define ANADIG_REG_1P1_CLR 0x118
#define ANADIG_ANA_MISC0 0x150
#define ANADIG_ANA_MISC0_SET 0x154
#define ANADIG_ANA_MISC0_CLR 0x158
@ -117,6 +125,42 @@
#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
/* System Integration Module (SIM) Registers */
#define SIM_GPR1 0x30
#define USB_PHY_VLLS_WAKEUP_EN BIT(0)
#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18)
#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19)
#define BM_ANADIG_PLL_USB2_HOLD_RING_OFF BIT(11)
/* DCD module, the offset is 0x800 */
#define DCD_CONTROL 0x800
#define DCD_CLOCK (DCD_CONTROL + 0x4)
#define DCD_STATUS (DCD_CONTROL + 0x8)
#define DCD_CONTROL_SR BIT(25)
#define DCD_CONTROL_START BIT(24)
#define DCD_CONTROL_BC12 BIT(17)
#define DCD_CONTROL_IE BIT(16)
#define DCD_CONTROL_IF BIT(8)
#define DCD_CONTROL_IACK BIT(0)
#define DCD_CLOCK_MHZ BIT(0)
#define DCD_STATUS_ACTIVE BIT(22)
#define DCD_STATUS_TO BIT(21)
#define DCD_STATUS_ERR BIT(20)
#define DCD_STATUS_SEQ_STAT (BIT(18) | BIT(19))
#define DCD_CHG_PORT BIT(19)
#define DCD_CHG_DET (BIT(18) | BIT(19))
#define DCD_CHG_DPIN BIT(18)
#define DCD_STATUS_SEQ_RES (BIT(16) | BIT(17))
#define DCD_SDP_PORT BIT(16)
#define DCD_CDP_PORT BIT(17)
#define DCD_DCP_PORT (BIT(16) | BIT(17))
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
/* Do disconnection between PHY and controller without vbus */
@ -149,6 +193,19 @@
#define MXS_PHY_TX_D_CAL_MIN 79
#define MXS_PHY_TX_D_CAL_MAX 119
/*
* At some versions, the PHY2's clock is controlled by hardware directly,
* eg, according to PHY's suspend status. In these PHYs, we only need to
* open the clock at the initialization and close it at its shutdown routine.
* It will be benefit for remote wakeup case which needs to send resume
* signal as soon as possible, and in this case, the resume signal can be sent
* out without software interfere.
*/
#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4)
/* The MXS PHYs which have DCD module for charger detection */
#define MXS_PHY_HAS_DCD BIT(5)
struct mxs_phy_data {
unsigned int flags;
};
@ -160,12 +217,14 @@ static const struct mxs_phy_data imx23_phy_data = {
static const struct mxs_phy_data imx6q_phy_data = {
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_NEED_IP_FIX,
MXS_PHY_NEED_IP_FIX |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6sl_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_NEED_IP_FIX,
MXS_PHY_NEED_IP_FIX |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data vf610_phy_data = {
@ -174,14 +233,17 @@ static const struct mxs_phy_data vf610_phy_data = {
};
static const struct mxs_phy_data imx6sx_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6ul_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx7ulp_phy_data = {
.flags = MXS_PHY_HAS_DCD,
};
static const struct of_device_id mxs_phy_dt_ids[] = {
@ -201,9 +263,14 @@ struct mxs_phy {
struct clk *clk;
const struct mxs_phy_data *data;
struct regmap *regmap_anatop;
struct regmap *regmap_sim;
int port_id;
u32 tx_reg_set;
u32 tx_reg_mask;
struct regulator *phy_3p0;
bool hardware_control_phy2_clk;
enum usb_current_mode mode;
unsigned long clk_rate;
};
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@ -221,6 +288,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
return mxs_phy->data == &imx7ulp_phy_data;
}
static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy)
{
return mxs_phy->data == &imx6ul_phy_data;
}
/*
* PHY needs some 32K cycles to switch from 32K clock to
* bus (such as AHB/AXI, etc) clock.
@ -288,6 +360,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (ret)
goto disable_pll;
if (mxs_phy->phy_3p0) {
ret = regulator_enable(mxs_phy->phy_3p0);
if (ret) {
dev_err(mxs_phy->phy.dev,
"Failed to enable 3p0 regulator, ret=%d\n",
ret);
return ret;
}
}
/* Power up the PHY */
writel(0, base + HW_USBPHY_PWD);
@ -386,21 +468,10 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
usleep_range(500, 1000);
}
static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
{
void __iomem *base = mxs_phy->phy.io_priv;
u32 phyctrl = readl(base + HW_USBPHY_CTRL);
if (IS_ENABLED(CONFIG_USB_OTG) &&
!(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
return true;
return false;
}
static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
{
bool vbus_is_on = false;
enum usb_phy_events last_event = mxs_phy->phy.last_event;
/* If the SoCs don't need to disconnect line without vbus, quit */
if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
@ -412,7 +483,8 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
if (on && ((!vbus_is_on && mxs_phy->mode != CUR_USB_MODE_HOST) ||
(last_event == USB_EVENT_VBUS)))
__mxs_phy_disconnect_line(mxs_phy, true);
else
__mxs_phy_disconnect_line(mxs_phy, false);
@ -453,6 +525,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
if (is_imx7ulp_phy(mxs_phy))
mxs_phy_pll_enable(phy->io_priv, false);
if (mxs_phy->phy_3p0)
regulator_disable(mxs_phy->phy_3p0);
clk_disable_unprepare(mxs_phy->clk);
}
@ -506,14 +581,49 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
} else {
writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
}
/*
* USB2 PLL use ring VCO, when the PLL power up, the ring
* VCOs supply also ramp up. There is a possibility that
* the ring VCO start oscillation at multi nodes in this
* phase, especially for VCO which has many stages, then
* the multiwave will be kept until PLL power down. the bit
* hold_ring_off can force the VCO in one determined state
* to avoid the multiwave issue when VCO supply start ramp
* up.
*/
if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop)
regmap_write(mxs_phy->regmap_anatop,
ANADIG_PLL_USB2_SET,
BM_ANADIG_PLL_USB2_HOLD_RING_OFF);
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_SET);
clk_disable_unprepare(mxs_phy->clk);
if (!(mxs_phy->port_id == 1 &&
mxs_phy->hardware_control_phy2_clk))
clk_disable_unprepare(mxs_phy->clk);
} else {
mxs_phy_clock_switch_delay();
ret = clk_prepare_enable(mxs_phy->clk);
if (ret)
return ret;
if (!(mxs_phy->port_id == 1 &&
mxs_phy->hardware_control_phy2_clk)) {
ret = clk_prepare_enable(mxs_phy->clk);
if (ret)
return ret;
}
/*
* Per IC design's requirement, hold_ring_off bit can be
* cleared 25us after PLL power up and 25us before any USB
* TX/RX.
*/
if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop) {
udelay(25);
regmap_write(mxs_phy->regmap_anatop,
ANADIG_PLL_USB2_CLR,
BM_ANADIG_PLL_USB2_HOLD_RING_OFF);
udelay(25);
}
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_CLR);
writel(0, x->io_priv + HW_USBPHY_PWD);
@ -708,6 +818,169 @@ static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy)
return chgr_type;
}
static int mxs_phy_on_suspend(struct usb_phy *phy,
enum usb_device_speed speed)
{
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
dev_dbg(phy->dev, "%s device has suspended\n",
(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
/* delay 4ms to wait bus entering idle */
usleep_range(4000, 5000);
if (mxs_phy->data->flags & MXS_PHY_ABNORMAL_IN_SUSPEND) {
writel_relaxed(0xffffffff, phy->io_priv + HW_USBPHY_PWD);
writel_relaxed(0, phy->io_priv + HW_USBPHY_PWD);
}
if (speed == USB_SPEED_HIGH)
writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
phy->io_priv + HW_USBPHY_CTRL_CLR);
return 0;
}
/*
* The resume signal must be finished here.
*/
static int mxs_phy_on_resume(struct usb_phy *phy,
enum usb_device_speed speed)
{
dev_dbg(phy->dev, "%s device has resumed\n",
(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
if (speed == USB_SPEED_HIGH) {
/* Make sure the device has switched to High-Speed mode */
udelay(500);
writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
phy->io_priv + HW_USBPHY_CTRL_SET);
}
return 0;
}
/*
* Set the usb current role for phy.
*/
static int mxs_phy_set_mode(struct usb_phy *phy,
enum usb_current_mode mode)
{
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
mxs_phy->mode = mode;
return 0;
}
static int mxs_phy_dcd_start(struct mxs_phy *mxs_phy)
{
void __iomem *base = mxs_phy->phy.io_priv;
u32 value;
value = readl(base + DCD_CONTROL);
writel(value | DCD_CONTROL_SR, base + DCD_CONTROL);
if (!mxs_phy->clk_rate)
return -EINVAL;
value = readl(base + DCD_CONTROL);
writel(((mxs_phy->clk_rate / 1000000) << 2) | DCD_CLOCK_MHZ,
base + DCD_CLOCK);
value = readl(base + DCD_CONTROL);
value &= ~DCD_CONTROL_IE;
writel(value | DCD_CONTROL_BC12, base + DCD_CONTROL);
value = readl(base + DCD_CONTROL);
writel(value | DCD_CONTROL_START, base + DCD_CONTROL);
return 0;
}
#define DCD_CHARGING_DURTION 1000 /* One second according to BC 1.2 */
static enum usb_charger_type mxs_phy_dcd_flow(struct usb_phy *phy)
{
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
void __iomem *base = mxs_phy->phy.io_priv;
u32 value;
int i = 0;
enum usb_charger_type chgr_type;
if (mxs_phy_dcd_start(mxs_phy))
return UNKNOWN_TYPE;
while (i++ <= (DCD_CHARGING_DURTION / 50)) {
value = readl(base + DCD_CONTROL);
if (value & DCD_CONTROL_IF) {
value = readl(base + DCD_STATUS);
if (value & DCD_STATUS_ACTIVE) {
dev_err(phy->dev, "still detecting\n");
chgr_type = UNKNOWN_TYPE;
break;
}
if (value & DCD_STATUS_TO) {
dev_err(phy->dev, "detect timeout\n");
chgr_type = UNKNOWN_TYPE;
break;
}
if (value & DCD_STATUS_ERR) {
dev_err(phy->dev, "detect error\n");
chgr_type = UNKNOWN_TYPE;
break;
}
if ((value & DCD_STATUS_SEQ_STAT) <= DCD_CHG_DPIN) {
dev_err(phy->dev, "error occurs\n");
chgr_type = UNKNOWN_TYPE;
break;
}
/* SDP */
if (((value & DCD_STATUS_SEQ_STAT) == DCD_CHG_PORT) &&
((value & DCD_STATUS_SEQ_RES)
== DCD_SDP_PORT)) {
dev_dbg(phy->dev, "SDP\n");
chgr_type = SDP_TYPE;
break;
}
if ((value & DCD_STATUS_SEQ_STAT) == DCD_CHG_DET) {
if ((value & DCD_STATUS_SEQ_RES) ==
DCD_CDP_PORT) {
dev_dbg(phy->dev, "CDP\n");
chgr_type = CDP_TYPE;
break;
}
if ((value & DCD_STATUS_SEQ_RES) ==
DCD_DCP_PORT) {
dev_dbg(phy->dev, "DCP\n");
chgr_type = DCP_TYPE;
break;
}
}
dev_err(phy->dev, "unknown error occurs\n");
chgr_type = UNKNOWN_TYPE;
break;
}
msleep(50);
}
if (i > 20) {
dev_err(phy->dev, "charger detecting timeout\n");
chgr_type = UNKNOWN_TYPE;
}
/* disable dcd module */
readl(base + DCD_STATUS);
writel(DCD_CONTROL_IACK, base + DCD_CONTROL);
writel(DCD_CONTROL_SR, base + DCD_CONTROL);
return chgr_type;
}
static int mxs_phy_probe(struct platform_device *pdev)
{
struct resource *res;
@ -739,6 +1012,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
if (!mxs_phy)
return -ENOMEM;
mxs_phy->clk_rate = clk_get_rate(clk);
/* Some SoCs don't have anatop registers */
if (of_get_property(np, "fsl,anatop", NULL)) {
mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle
@ -750,6 +1024,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
}
/* Currently, only imx7ulp has SIM module */
if (of_get_property(np, "nxp,sim", NULL)) {
mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle
(np, "nxp,sim");
if (IS_ERR(mxs_phy->regmap_sim)) {
dev_dbg(&pdev->dev,
"failed to find regmap for sim\n");
return PTR_ERR(mxs_phy->regmap_sim);
}
}
/* Precompute which bits of the TX register are to be updated, if any */
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
@ -784,6 +1069,8 @@ static int mxs_phy_probe(struct platform_device *pdev)
ret = of_alias_get_id(np, "usbphy");
if (ret < 0)
dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
mxs_phy->clk = clk;
mxs_phy->data = of_id->data;
mxs_phy->port_id = ret;
mxs_phy->phy.io_priv = base;
@ -796,10 +1083,33 @@ static int mxs_phy_probe(struct platform_device *pdev)
mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect;
mxs_phy->phy.type = USB_PHY_TYPE_USB2;
mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup;
mxs_phy->phy.charger_detect = mxs_phy_charger_detect;
if (mxs_phy->data->flags & MXS_PHY_HAS_DCD)
mxs_phy->phy.charger_detect = mxs_phy_dcd_flow;
else
mxs_phy->phy.charger_detect = mxs_phy_charger_detect;
mxs_phy->clk = clk;
mxs_phy->data = of_id->data;
mxs_phy->phy.set_mode = mxs_phy_set_mode;
if (mxs_phy->data->flags & MXS_PHY_SENDING_SOF_TOO_FAST) {
mxs_phy->phy.notify_suspend = mxs_phy_on_suspend;
mxs_phy->phy.notify_resume = mxs_phy_on_resume;
}
mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0");
if (PTR_ERR(mxs_phy->phy_3p0) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV) {
/* not exist */
mxs_phy->phy_3p0 = NULL;
} else if (IS_ERR(mxs_phy->phy_3p0)) {
dev_err(&pdev->dev, "Getting regulator error: %ld\n",
PTR_ERR(mxs_phy->phy_3p0));
return PTR_ERR(mxs_phy->phy_3p0);
}
if (mxs_phy->phy_3p0)
regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000);
if (mxs_phy->data->flags & MXS_PHY_HARDWARE_CONTROL_PHY2_CLK)
mxs_phy->hardware_control_phy2_clk = true;
platform_set_drvdata(pdev, mxs_phy);
@ -818,28 +1128,58 @@ static int mxs_phy_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on)
{
u32 mask = USB_PHY_VLLS_WAKEUP_EN;
/* If the SoCs don't have SIM, quit */
if (!mxs_phy->regmap_sim)
return;
if (on) {
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask);
udelay(500);
} else {
regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0);
}
}
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
{
unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
unsigned int reg;
u32 value;
/* If the SoCs don't have anatop, quit */
if (!mxs_phy->regmap_anatop)
return;
if (is_imx6q_phy(mxs_phy))
if (is_imx6q_phy(mxs_phy)) {
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop, reg,
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
else if (is_imx6sl_phy(mxs_phy))
} else if (is_imx6sl_phy(mxs_phy)) {
reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop,
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
} else if (is_imx6ul_phy(mxs_phy)) {
reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR;
value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG |
BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP;
if (mxs_phy_get_vbus_status(mxs_phy) && on)
regmap_write(mxs_phy->regmap_anatop, reg, value);
else if (!on)
regmap_write(mxs_phy->regmap_anatop, reg, value);
}
}
static int mxs_phy_system_suspend(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
mxs_phy_wakeup_enable(mxs_phy, true);
}
return 0;
}
@ -848,8 +1188,10 @@ static int mxs_phy_system_resume(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
mxs_phy_wakeup_enable(mxs_phy, false);
}
return 0;
}

View File

@ -34,6 +34,14 @@ struct phy_devm {
struct notifier_block *nb;
};
static const char *const usb_chger_type[] = {
[UNKNOWN_TYPE] = "USB_CHARGER_UNKNOWN_TYPE",
[SDP_TYPE] = "USB_CHARGER_SDP_TYPE",
[CDP_TYPE] = "USB_CHARGER_CDP_TYPE",
[DCP_TYPE] = "USB_CHARGER_DCP_TYPE",
[ACA_TYPE] = "USB_CHARGER_ACA_TYPE",
};
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@ -98,7 +106,8 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
{
struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work);
char uchger_state[50] = { 0 };
char *envp[] = { uchger_state, NULL };
char uchger_type[50] = { 0 };
char *envp[] = { uchger_state, uchger_type, NULL };
unsigned int min, max;
switch (usb_phy->chg_state) {
@ -122,6 +131,8 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
return;
}
snprintf(uchger_type, ARRAY_SIZE(uchger_type),
"USB_CHARGER_TYPE=%s", usb_chger_type[usb_phy->chg_type]);
kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
}

View File

@ -63,6 +63,13 @@ enum usb_otg_state {
OTG_STATE_A_VBUS_ERR,
};
/* The usb role of phy to be working with */
enum usb_current_mode {
CUR_USB_MODE_NONE,
CUR_USB_MODE_HOST,
CUR_USB_MODE_DEVICE,
};
struct usb_phy;
struct usb_otg;
@ -155,6 +162,15 @@ struct usb_phy {
* manually detect the charger type.
*/
enum usb_charger_type (*charger_detect)(struct usb_phy *x);
int (*notify_suspend)(struct usb_phy *x,
enum usb_device_speed speed);
int (*notify_resume)(struct usb_phy *x,
enum usb_device_speed speed);
int (*set_mode)(struct usb_phy *x,
enum usb_current_mode mode);
};
/* for board-specific init logic */
@ -213,6 +229,15 @@ usb_phy_vbus_off(struct usb_phy *x)
return x->set_vbus(x, false);
}
static inline int
usb_phy_set_mode(struct usb_phy *x, enum usb_current_mode mode)
{
if (!x || !x->set_mode)
return 0;
return x->set_mode(x, mode);
}
/* for usb host and peripheral controller drivers */
#if IS_ENABLED(CONFIG_USB_PHY)
extern struct usb_phy *usb_get_phy(enum usb_phy_type type);
@ -334,6 +359,24 @@ usb_phy_notify_disconnect(struct usb_phy *x, enum usb_device_speed speed)
return 0;
}
static inline int usb_phy_notify_suspend
(struct usb_phy *x, enum usb_device_speed speed)
{
if (x && x->notify_suspend)
return x->notify_suspend(x, speed);
else
return 0;
}
static inline int usb_phy_notify_resume
(struct usb_phy *x, enum usb_device_speed speed)
{
if (x && x->notify_resume)
return x->notify_resume(x, speed);
else
return 0;
}
/* notifiers */
static inline int
usb_register_notifier(struct usb_phy *x, struct notifier_block *nb)