diff --git a/Documentation/ABI/testing/usb-charger-uevent b/Documentation/ABI/testing/usb-charger-uevent new file mode 100644 index 000000000000..93ffd3a54a7f --- /dev/null +++ b/Documentation/ABI/testing/usb-charger-uevent @@ -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 diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 24b4f091acb8..4b2002dabcdf 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -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 diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 70b8c8248caf..299eb6be79b8 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -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 * on behalf of DENX Software Engineering GmbH */ @@ -18,6 +19,7 @@ #include #include #include +#include #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 + * VCO’s 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; } diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 0277f62739a2..ad2554630889 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -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); } diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index e4de6bc1f69b..086f95f7b424 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -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)