mirror of
https://github.com/brain-hackers/linux-brain.git
synced 2024-06-09 23:36:23 +09:00
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:
commit
b4e4776fd6
45
Documentation/ABI/testing/usb-charger-uevent
Normal file
45
Documentation/ABI/testing/usb-charger-uevent
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
* 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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user