diff options
Diffstat (limited to 'drivers/usb/phy/phy-mxs-usb.c')
-rw-r--r-- | drivers/usb/phy/phy-mxs-usb.c | 446 |
1 files changed, 413 insertions, 33 deletions
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 70b8c8248caf..4ac3746f769c 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 <marex@denx.de> * on behalf of DENX Software Engineering GmbH */ @@ -18,6 +19,8 @@ #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <linux/iopoll.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> #define DRIVER_NAME "mxs_phy" @@ -70,6 +73,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 +126,46 @@ #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_TIMER1 (DCD_CONTROL + 0x14) + +#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 DCD_TVDPSRC_ON_MASK GENMASK(9, 0) +#define DCD_TVDPSRC_ON_VALUE 0xf0 /* 240ms */ + #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) /* Do disconnection between PHY and controller without vbus */ @@ -149,6 +198,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 +222,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 +238,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 +268,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 +293,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 +365,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 +473,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 +488,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 +530,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 +586,51 @@ 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); + pm_runtime_put(x->dev); } else { + pm_runtime_get_sync(x->dev); 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 +825,174 @@ 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_TIMER1); + value &= ~DCD_TVDPSRC_ON_MASK; + value |= DCD_TVDPSRC_ON_VALUE; + writel(value, base + DCD_TIMER1); + + 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 +1024,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 +1036,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 +1081,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,15 +1095,42 @@ 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); device_set_wakeup_capable(&pdev->dev, true); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); + return usb_add_phy_dev(&mxs_phy->phy); } @@ -813,33 +1139,68 @@ static int mxs_phy_remove(struct platform_device *pdev) struct mxs_phy *mxs_phy = platform_get_drvdata(pdev); usb_remove_phy(&mxs_phy->phy); + pm_runtime_get_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + pm_runtime_put_noidle(&pdev->dev); return 0; } +#ifdef CONFIG_PM + #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,15 +1209,34 @@ 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); + } + + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); return 0; } #endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend, - mxs_phy_system_resume); +static int mxs_phy_runtime_resume(struct device *dev) +{ + return 0; +} + +static int mxs_phy_runtime_suspend(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops mxs_phy_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mxs_phy_system_suspend, mxs_phy_system_resume) + SET_RUNTIME_PM_OPS(mxs_phy_runtime_suspend, mxs_phy_runtime_resume, NULL) +}; static struct platform_driver mxs_phy_driver = { .probe = mxs_phy_probe, @@ -864,7 +1244,7 @@ static struct platform_driver mxs_phy_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = mxs_phy_dt_ids, - .pm = &mxs_phy_pm, + .pm = &mxs_phy_pm_ops, }, }; |