Merge tag 'usb-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB/PHY updates from Greg KH:
 "Here is the big USB/PHY driver patches for 4.20-rc1

  Lots of USB changes in here, primarily in these areas:

   - typec updates and new drivers

   - new PHY drivers

   - dwc2 driver updates and additions (this old core keeps getting
     added to new devices.)

   - usbtmc major update based on the industry group coming together and
     working to add new features and performance to the driver.

   - USB gadget additions for new features

   - USB gadget configfs updates

   - chipidea driver updates

   - other USB gadget updates

   - USB serial driver updates

   - renesas driver updates

   - xhci driver updates

   - other tiny USB driver updates

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (229 commits)
  usb: phy: ab8500: silence some uninitialized variable warnings
  usb: xhci: tegra: Add genpd support
  usb: xhci: tegra: Power-off power-domains on removal
  usbip:vudc: BUG kmalloc-2048 (Not tainted): Poison overwritten
  usbip: tools: fix atoi() on non-null terminated string
  USB: misc: appledisplay: fix backlight update_status return code
  phy: phy-pxa-usb: add a new driver
  usb: host: add DT bindings for faraday fotg2
  usb: host: ohci-at91: fix request of irq for optional gpio
  usb/early: remove set but not used variable 'remain_length'
  usb: typec: Fix copy/paste on typec_set_vconn_role() kerneldoc
  usb: typec: tcpm: Report back negotiated PPS voltage and current
  USB: core: remove set but not used variable 'udev'
  usb: core: fix memory leak on port_dev_path allocation
  USB: net2280: Remove ->disconnect() callback from net2280_pullup()
  usb: dwc2: disable power_down on rockchip devices
  usb: gadget: udc: renesas_usb3: add support for r8a77990
  dt-bindings: usb: renesas_usb3: add bindings for r8a77990
  usb: gadget: udc: renesas_usb3: Add r8a774a1 support
  USB: serial: cypress_m8: remove set but not used variable 'iflag'
  ...
This commit is contained in:
Linus Torvalds
2018-10-26 08:14:13 -07:00
181 changed files with 8807 additions and 2209 deletions

View File

@@ -43,6 +43,7 @@ config PHY_XGENE
source "drivers/phy/allwinner/Kconfig"
source "drivers/phy/amlogic/Kconfig"
source "drivers/phy/broadcom/Kconfig"
source "drivers/phy/cadence/Kconfig"
source "drivers/phy/hisilicon/Kconfig"
source "drivers/phy/lantiq/Kconfig"
source "drivers/phy/marvell/Kconfig"
@@ -54,6 +55,7 @@ source "drivers/phy/ralink/Kconfig"
source "drivers/phy/renesas/Kconfig"
source "drivers/phy/rockchip/Kconfig"
source "drivers/phy/samsung/Kconfig"
source "drivers/phy/socionext/Kconfig"
source "drivers/phy/st/Kconfig"
source "drivers/phy/tegra/Kconfig"
source "drivers/phy/ti/Kconfig"

View File

@@ -15,6 +15,7 @@ obj-$(CONFIG_ARCH_RENESAS) += renesas/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-y += broadcom/ \
cadence/ \
hisilicon/ \
marvell/ \
motorola/ \
@@ -22,5 +23,6 @@ obj-y += broadcom/ \
qualcomm/ \
ralink/ \
samsung/ \
socionext/ \
st/ \
ti/

View File

@@ -60,7 +60,8 @@ config PHY_NS2_USB_DRD
config PHY_BRCM_SATA
tristate "Broadcom SATA PHY driver"
depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || COMPILE_TEST
depends on ARCH_BRCMSTB || ARCH_BCM_IPROC || BMIPS_GENERIC || \
ARCH_BCM_63XX || COMPILE_TEST
depends on OF
select GENERIC_PHY
default ARCH_BCM_IPROC

View File

@@ -153,8 +153,8 @@ static int cygnus_pcie_phy_probe(struct platform_device *pdev)
struct cygnus_pcie_phy *p;
if (of_property_read_u32(child, "reg", &id)) {
dev_err(dev, "missing reg property for %s\n",
child->name);
dev_err(dev, "missing reg property for %pOFn\n",
child);
ret = -EINVAL;
goto put_child;
}

View File

@@ -47,6 +47,7 @@ enum brcm_sata_phy_version {
BRCM_SATA_PHY_IPROC_NS2,
BRCM_SATA_PHY_IPROC_NSP,
BRCM_SATA_PHY_IPROC_SR,
BRCM_SATA_PHY_DSL_28NM,
};
enum brcm_sata_phy_rxaeq_mode {
@@ -96,7 +97,10 @@ enum sata_phy_regs {
PLLCONTROL_0_FREQ_DET_RESTART = BIT(13),
PLLCONTROL_0_FREQ_MONITOR = BIT(12),
PLLCONTROL_0_SEQ_START = BIT(15),
PLL_CAP_CHARGE_TIME = 0x83,
PLL_VCO_CAL_THRESH = 0x84,
PLL_CAP_CONTROL = 0x85,
PLL_FREQ_DET_TIME = 0x86,
PLL_ACTRL2 = 0x8b,
PLL_ACTRL2_SELDIV_MASK = 0x1f,
PLL_ACTRL2_SELDIV_SHIFT = 9,
@@ -106,6 +110,9 @@ enum sata_phy_regs {
PLL1_ACTRL2 = 0x82,
PLL1_ACTRL3 = 0x83,
PLL1_ACTRL4 = 0x84,
PLL1_ACTRL5 = 0x85,
PLL1_ACTRL6 = 0x86,
PLL1_ACTRL7 = 0x87,
TX_REG_BANK = 0x070,
TX_ACTRL0 = 0x80,
@@ -119,6 +126,8 @@ enum sata_phy_regs {
AEQ_FRC_EQ_FORCE = BIT(0),
AEQ_FRC_EQ_FORCE_VAL = BIT(1),
AEQRX_REG_BANK_1 = 0xe0,
AEQRX_SLCAL0_CTRL0 = 0x82,
AEQRX_SLCAL1_CTRL0 = 0x86,
OOB_REG_BANK = 0x150,
OOB1_REG_BANK = 0x160,
@@ -168,6 +177,7 @@ static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port)
switch (priv->version) {
case BRCM_SATA_PHY_STB_28NM:
case BRCM_SATA_PHY_IPROC_NS2:
case BRCM_SATA_PHY_DSL_28NM:
size = SATA_PCB_REG_28NM_SPACE_SIZE;
break;
case BRCM_SATA_PHY_STB_40NM:
@@ -482,6 +492,61 @@ static int brcm_sr_sata_init(struct brcm_sata_port *port)
return 0;
}
static int brcm_dsl_sata_init(struct brcm_sata_port *port)
{
void __iomem *base = brcm_sata_pcb_base(port);
struct device *dev = port->phy_priv->dev;
unsigned int try;
u32 tmp;
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL7, 0, 0x873);
brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL6, 0, 0xc000);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0,
0, 0x3089);
usleep_range(1000, 2000);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_REG_BANK_0_PLLCONTROL_0,
0, 0x3088);
usleep_range(1000, 2000);
brcm_sata_phy_wr(base, AEQRX_REG_BANK_1, AEQRX_SLCAL0_CTRL0,
0, 0x3000);
brcm_sata_phy_wr(base, AEQRX_REG_BANK_1, AEQRX_SLCAL1_CTRL0,
0, 0x3000);
usleep_range(1000, 2000);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_CAP_CHARGE_TIME, 0, 0x32);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_VCO_CAL_THRESH, 0, 0xa);
brcm_sata_phy_wr(base, PLL_REG_BANK_0, PLL_FREQ_DET_TIME, 0, 0x64);
usleep_range(1000, 2000);
/* Acquire PLL lock */
try = 50;
while (try) {
tmp = brcm_sata_phy_rd(base, BLOCK0_REG_BANK,
BLOCK0_XGXSSTATUS);
if (tmp & BLOCK0_XGXSSTATUS_PLL_LOCK)
break;
msleep(20);
try--;
};
if (!try) {
/* PLL did not lock; give up */
dev_err(dev, "port%d PLL did not lock\n", port->portnum);
return -ETIMEDOUT;
}
dev_dbg(dev, "port%d initialized\n", port->portnum);
return 0;
}
static int brcm_sata_phy_init(struct phy *phy)
{
int rc;
@@ -501,6 +566,9 @@ static int brcm_sata_phy_init(struct phy *phy)
case BRCM_SATA_PHY_IPROC_SR:
rc = brcm_sr_sata_init(port);
break;
case BRCM_SATA_PHY_DSL_28NM:
rc = brcm_dsl_sata_init(port);
break;
default:
rc = -ENODEV;
}
@@ -552,6 +620,8 @@ static const struct of_device_id brcm_sata_phy_of_match[] = {
.data = (void *)BRCM_SATA_PHY_IPROC_NSP },
{ .compatible = "brcm,iproc-sr-sata-phy",
.data = (void *)BRCM_SATA_PHY_IPROC_SR },
{ .compatible = "brcm,bcm63138-sata-phy",
.data = (void *)BRCM_SATA_PHY_DSL_28NM },
{},
};
MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match);
@@ -600,8 +670,8 @@ static int brcm_sata_phy_probe(struct platform_device *pdev)
struct brcm_sata_port *port;
if (of_property_read_u32(child, "reg", &id)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
dev_err(dev, "missing reg property in node %pOFn\n",
child);
ret = -EINVAL;
goto put_child;
}

View File

@@ -372,10 +372,8 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
clk_disable(priv->usb_30_clk);
phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
return 0;
return PTR_ERR_OR_ZERO(phy_provider);
}
#ifdef CONFIG_PM_SLEEP

View File

@@ -0,0 +1,10 @@
#
# Phy driver for Cadence MHDP DisplayPort controller
#
config PHY_CADENCE_DP
tristate "Cadence MHDP DisplayPort PHY driver"
depends on OF
depends on HAS_IOMEM
select GENERIC_PHY
help
Support for Cadence MHDP DisplayPort PHY.

View File

@@ -0,0 +1 @@
obj-$(CONFIG_PHY_CADENCE_DP) += phy-cadence-dp.o

View File

@@ -0,0 +1,541 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Cadence MHDP DisplayPort SD0801 PHY driver.
*
* Copyright 2018 Cadence Design Systems, Inc.
*
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#define DEFAULT_NUM_LANES 2
#define MAX_NUM_LANES 4
#define DEFAULT_MAX_BIT_RATE 8100 /* in Mbps */
#define POLL_TIMEOUT_US 2000
#define LANE_MASK 0x7
/*
* register offsets from DPTX PHY register block base (i.e MHDP
* register base + 0x30a00)
*/
#define PHY_AUX_CONFIG 0x00
#define PHY_AUX_CTRL 0x04
#define PHY_RESET 0x20
#define PHY_PMA_XCVR_PLLCLK_EN 0x24
#define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28
#define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c
#define PHY_POWER_STATE_LN_0 0x0000
#define PHY_POWER_STATE_LN_1 0x0008
#define PHY_POWER_STATE_LN_2 0x0010
#define PHY_POWER_STATE_LN_3 0x0018
#define PHY_PMA_XCVR_POWER_STATE_ACK 0x30
#define PHY_PMA_CMN_READY 0x34
#define PHY_PMA_XCVR_TX_VMARGIN 0x38
#define PHY_PMA_XCVR_TX_DEEMPH 0x3c
/*
* register offsets from SD0801 PHY register block base (i.e MHDP
* register base + 0x500000)
*/
#define CMN_SSM_BANDGAP_TMR 0x00084
#define CMN_SSM_BIAS_TMR 0x00088
#define CMN_PLLSM0_PLLPRE_TMR 0x000a8
#define CMN_PLLSM0_PLLLOCK_TMR 0x000b0
#define CMN_PLLSM1_PLLPRE_TMR 0x000c8
#define CMN_PLLSM1_PLLLOCK_TMR 0x000d0
#define CMN_BGCAL_INIT_TMR 0x00190
#define CMN_BGCAL_ITER_TMR 0x00194
#define CMN_IBCAL_INIT_TMR 0x001d0
#define CMN_PLL0_VCOCAL_INIT_TMR 0x00210
#define CMN_PLL0_VCOCAL_ITER_TMR 0x00214
#define CMN_PLL0_VCOCAL_REFTIM_START 0x00218
#define CMN_PLL0_VCOCAL_PLLCNT_START 0x00220
#define CMN_PLL0_INTDIV_M0 0x00240
#define CMN_PLL0_FRACDIVL_M0 0x00244
#define CMN_PLL0_FRACDIVH_M0 0x00248
#define CMN_PLL0_HIGH_THR_M0 0x0024c
#define CMN_PLL0_DSM_DIAG_M0 0x00250
#define CMN_PLL0_LOCK_PLLCNT_START 0x00278
#define CMN_PLL1_VCOCAL_INIT_TMR 0x00310
#define CMN_PLL1_VCOCAL_ITER_TMR 0x00314
#define CMN_PLL1_DSM_DIAG_M0 0x00350
#define CMN_TXPUCAL_INIT_TMR 0x00410
#define CMN_TXPUCAL_ITER_TMR 0x00414
#define CMN_TXPDCAL_INIT_TMR 0x00430
#define CMN_TXPDCAL_ITER_TMR 0x00434
#define CMN_RXCAL_INIT_TMR 0x00450
#define CMN_RXCAL_ITER_TMR 0x00454
#define CMN_SD_CAL_INIT_TMR 0x00490
#define CMN_SD_CAL_ITER_TMR 0x00494
#define CMN_SD_CAL_REFTIM_START 0x00498
#define CMN_SD_CAL_PLLCNT_START 0x004a0
#define CMN_PDIAG_PLL0_CTRL_M0 0x00680
#define CMN_PDIAG_PLL0_CLK_SEL_M0 0x00684
#define CMN_PDIAG_PLL0_CP_PADJ_M0 0x00690
#define CMN_PDIAG_PLL0_CP_IADJ_M0 0x00694
#define CMN_PDIAG_PLL0_FILT_PADJ_M0 0x00698
#define CMN_PDIAG_PLL0_CP_PADJ_M1 0x006d0
#define CMN_PDIAG_PLL0_CP_IADJ_M1 0x006d4
#define CMN_PDIAG_PLL1_CLK_SEL_M0 0x00704
#define XCVR_DIAG_PLLDRC_CTRL 0x10394
#define XCVR_DIAG_HSCLK_SEL 0x10398
#define XCVR_DIAG_HSCLK_DIV 0x1039c
#define TX_PSC_A0 0x10400
#define TX_PSC_A1 0x10404
#define TX_PSC_A2 0x10408
#define TX_PSC_A3 0x1040c
#define RX_PSC_A0 0x20000
#define RX_PSC_A1 0x20004
#define RX_PSC_A2 0x20008
#define RX_PSC_A3 0x2000c
#define PHY_PLL_CFG 0x30038
struct cdns_dp_phy {
void __iomem *base; /* DPTX registers base */
void __iomem *sd_base; /* SD0801 registers base */
u32 num_lanes; /* Number of lanes to use */
u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
struct device *dev;
};
static int cdns_dp_phy_init(struct phy *phy);
static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
unsigned int lane);
static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy);
static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
unsigned int offset,
unsigned char start_bit,
unsigned char num_bits,
unsigned int val);
static const struct phy_ops cdns_dp_phy_ops = {
.init = cdns_dp_phy_init,
.owner = THIS_MODULE,
};
static int cdns_dp_phy_init(struct phy *phy)
{
unsigned char lane_bits;
struct cdns_dp_phy *cdns_phy = phy_get_drvdata(phy);
writel(0x0003, cdns_phy->base + PHY_AUX_CTRL); /* enable AUX */
/* PHY PMA registers configuration function */
cdns_dp_phy_pma_cfg(cdns_phy);
/*
* Set lines power state to A0
* Set lines pll clk enable to 0
*/
cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_0, 6, 0x0000);
if (cdns_phy->num_lanes >= 2) {
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_1, 6, 0x0000);
if (cdns_phy->num_lanes == 4) {
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_2, 6, 0);
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_POWER_STATE_REQ,
PHY_POWER_STATE_LN_3, 6, 0);
}
}
cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
0, 1, 0x0000);
if (cdns_phy->num_lanes >= 2) {
cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
1, 1, 0x0000);
if (cdns_phy->num_lanes == 4) {
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_PLLCLK_EN,
2, 1, 0x0000);
cdns_dp_phy_write_field(cdns_phy,
PHY_PMA_XCVR_PLLCLK_EN,
3, 1, 0x0000);
}
}
/*
* release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
* used lanes
*/
lane_bits = (1 << cdns_phy->num_lanes) - 1;
writel(((0xF & ~lane_bits) << 4) | (0xF & lane_bits),
cdns_phy->base + PHY_RESET);
/* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
writel(0x0001, cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN);
/* PHY PMA registers configuration functions */
cdns_dp_phy_pma_cmn_vco_cfg_25mhz(cdns_phy);
cdns_dp_phy_pma_cmn_rate(cdns_phy);
/* take out of reset */
cdns_dp_phy_write_field(cdns_phy, PHY_RESET, 8, 1, 1);
cdns_dp_phy_wait_pma_cmn_ready(cdns_phy);
cdns_dp_phy_run(cdns_phy);
return 0;
}
static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy)
{
unsigned int reg;
int ret;
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_CMN_READY, reg,
reg & 1, 0, 500);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for PMA common ready\n");
}
static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy)
{
unsigned int i;
/* PMA common configuration */
cdns_dp_phy_pma_cmn_cfg_25mhz(cdns_phy);
/* PMA lane configuration to deal with multi-link operation */
for (i = 0; i < cdns_phy->num_lanes; i++)
cdns_dp_phy_pma_lane_cfg(cdns_phy, i);
}
static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
{
/* refclock registers - assumes 25 MHz refclock */
writel(0x0019, cdns_phy->sd_base + CMN_SSM_BIAS_TMR);
writel(0x0032, cdns_phy->sd_base + CMN_PLLSM0_PLLPRE_TMR);
writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM0_PLLLOCK_TMR);
writel(0x0032, cdns_phy->sd_base + CMN_PLLSM1_PLLPRE_TMR);
writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM1_PLLLOCK_TMR);
writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_INIT_TMR);
writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_ITER_TMR);
writel(0x0019, cdns_phy->sd_base + CMN_IBCAL_INIT_TMR);
writel(0x001E, cdns_phy->sd_base + CMN_TXPUCAL_INIT_TMR);
writel(0x0006, cdns_phy->sd_base + CMN_TXPUCAL_ITER_TMR);
writel(0x001E, cdns_phy->sd_base + CMN_TXPDCAL_INIT_TMR);
writel(0x0006, cdns_phy->sd_base + CMN_TXPDCAL_ITER_TMR);
writel(0x02EE, cdns_phy->sd_base + CMN_RXCAL_INIT_TMR);
writel(0x0006, cdns_phy->sd_base + CMN_RXCAL_ITER_TMR);
writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_INIT_TMR);
writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_ITER_TMR);
writel(0x000E, cdns_phy->sd_base + CMN_SD_CAL_REFTIM_START);
writel(0x012B, cdns_phy->sd_base + CMN_SD_CAL_PLLCNT_START);
/* PLL registers */
writel(0x0409, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_PADJ_M0);
writel(0x1001, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_IADJ_M0);
writel(0x0F08, cdns_phy->sd_base + CMN_PDIAG_PLL0_FILT_PADJ_M0);
writel(0x0004, cdns_phy->sd_base + CMN_PLL0_DSM_DIAG_M0);
writel(0x00FA, cdns_phy->sd_base + CMN_PLL0_VCOCAL_INIT_TMR);
writel(0x0004, cdns_phy->sd_base + CMN_PLL0_VCOCAL_ITER_TMR);
writel(0x00FA, cdns_phy->sd_base + CMN_PLL1_VCOCAL_INIT_TMR);
writel(0x0004, cdns_phy->sd_base + CMN_PLL1_VCOCAL_ITER_TMR);
writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_REFTIM_START);
}
static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
{
/* Assumes 25 MHz refclock */
switch (cdns_phy->max_bit_rate) {
/* Setting VCO for 10.8GHz */
case 2700:
case 5400:
writel(0x01B0, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x0120, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
/* Setting VCO for 9.72GHz */
case 2430:
case 3240:
writel(0x0184, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0xCCCD, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x0104, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
/* Setting VCO for 8.64GHz */
case 2160:
case 4320:
writel(0x0159, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0x999A, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x00E7, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
/* Setting VCO for 8.1GHz */
case 8100:
writel(0x0144, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
writel(0x00D8, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
break;
}
writel(0x0002, cdns_phy->sd_base + CMN_PDIAG_PLL0_CTRL_M0);
writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_PLLCNT_START);
}
static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy)
{
unsigned int clk_sel_val = 0;
unsigned int hsclk_div_val = 0;
unsigned int i;
/* 16'h0000 for single DP link configuration */
writel(0x0000, cdns_phy->sd_base + PHY_PLL_CFG);
switch (cdns_phy->max_bit_rate) {
case 1620:
clk_sel_val = 0x0f01;
hsclk_div_val = 2;
break;
case 2160:
case 2430:
case 2700:
clk_sel_val = 0x0701;
hsclk_div_val = 1;
break;
case 3240:
clk_sel_val = 0x0b00;
hsclk_div_val = 2;
break;
case 4320:
case 5400:
clk_sel_val = 0x0301;
hsclk_div_val = 0;
break;
case 8100:
clk_sel_val = 0x0200;
hsclk_div_val = 0;
break;
}
writel(clk_sel_val, cdns_phy->sd_base + CMN_PDIAG_PLL0_CLK_SEL_M0);
/* PMA lane configuration to deal with multi-link operation */
for (i = 0; i < cdns_phy->num_lanes; i++) {
writel(hsclk_div_val,
cdns_phy->sd_base + (XCVR_DIAG_HSCLK_DIV | (i<<11)));
}
}
static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
unsigned int lane)
{
unsigned int lane_bits = (lane & LANE_MASK) << 11;
/* Writing Tx/Rx Power State Controllers registers */
writel(0x00FB, cdns_phy->sd_base + (TX_PSC_A0 | lane_bits));
writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A2 | lane_bits));
writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A3 | lane_bits));
writel(0x0000, cdns_phy->sd_base + (RX_PSC_A0 | lane_bits));
writel(0x0000, cdns_phy->sd_base + (RX_PSC_A2 | lane_bits));
writel(0x0000, cdns_phy->sd_base + (RX_PSC_A3 | lane_bits));
writel(0x0001, cdns_phy->sd_base + (XCVR_DIAG_PLLDRC_CTRL | lane_bits));
writel(0x0000, cdns_phy->sd_base + (XCVR_DIAG_HSCLK_SEL | lane_bits));
}
static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy)
{
unsigned int read_val;
u32 write_val1 = 0;
u32 write_val2 = 0;
u32 mask = 0;
int ret;
/*
* waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the
* master lane
*/
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN_ACK,
read_val, read_val & 1, 0, POLL_TIMEOUT_US);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for link PLL clock enable ack\n");
ndelay(100);
switch (cdns_phy->num_lanes) {
case 1: /* lane 0 */
write_val1 = 0x00000004;
write_val2 = 0x00000001;
mask = 0x0000003f;
break;
case 2: /* lane 0-1 */
write_val1 = 0x00000404;
write_val2 = 0x00000101;
mask = 0x00003f3f;
break;
case 4: /* lane 0-3 */
write_val1 = 0x04040404;
write_val2 = 0x01010101;
mask = 0x3f3f3f3f;
break;
}
writel(write_val1, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
read_val, (read_val & mask) == write_val1, 0,
POLL_TIMEOUT_US);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for link power state ack\n");
writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ndelay(100);
writel(write_val2, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
read_val, (read_val & mask) == write_val2, 0,
POLL_TIMEOUT_US);
if (ret == -ETIMEDOUT)
dev_err(cdns_phy->dev,
"timeout waiting for link power state ack\n");
writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
ndelay(100);
}
static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
unsigned int offset,
unsigned char start_bit,
unsigned char num_bits,
unsigned int val)
{
unsigned int read_val;
read_val = readl(cdns_phy->base + offset);
writel(((val << start_bit) | (read_val & ~(((1 << num_bits) - 1) <<
start_bit))), cdns_phy->base + offset);
}
static int cdns_dp_phy_probe(struct platform_device *pdev)
{
struct resource *regs;
struct cdns_dp_phy *cdns_phy;
struct device *dev = &pdev->dev;
struct phy_provider *phy_provider;
struct phy *phy;
int err;
cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
if (!cdns_phy)
return -ENOMEM;
cdns_phy->dev = &pdev->dev;
phy = devm_phy_create(dev, NULL, &cdns_dp_phy_ops);
if (IS_ERR(phy)) {
dev_err(dev, "failed to create DisplayPort PHY\n");
return PTR_ERR(phy);
}
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
cdns_phy->base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(cdns_phy->base))
return PTR_ERR(cdns_phy->base);
regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(cdns_phy->sd_base))
return PTR_ERR(cdns_phy->sd_base);
err = device_property_read_u32(dev, "num_lanes",
&(cdns_phy->num_lanes));
if (err)
cdns_phy->num_lanes = DEFAULT_NUM_LANES;
switch (cdns_phy->num_lanes) {
case 1:
case 2:
case 4:
/* valid number of lanes */
break;
default:
dev_err(dev, "unsupported number of lanes: %d\n",
cdns_phy->num_lanes);
return -EINVAL;
}
err = device_property_read_u32(dev, "max_bit_rate",
&(cdns_phy->max_bit_rate));
if (err)
cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
switch (cdns_phy->max_bit_rate) {
case 2160:
case 2430:
case 2700:
case 3240:
case 4320:
case 5400:
case 8100:
/* valid bit rate */
break;
default:
dev_err(dev, "unsupported max bit rate: %dMbps\n",
cdns_phy->max_bit_rate);
return -EINVAL;
}
phy_set_drvdata(phy, cdns_phy);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
cdns_phy->num_lanes,
cdns_phy->max_bit_rate / 1000,
cdns_phy->max_bit_rate % 1000);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct of_device_id cdns_dp_phy_of_match[] = {
{
.compatible = "cdns,dp-phy"
},
{}
};
MODULE_DEVICE_TABLE(of, cdns_dp_phy_of_match);
static struct platform_driver cdns_dp_phy_driver = {
.probe = cdns_dp_phy_probe,
.driver = {
.name = "cdns-dp-phy",
.of_match_table = cdns_dp_phy_of_match,
}
};
module_platform_driver(cdns_dp_phy_driver);
MODULE_AUTHOR("Cadence Design Systems, Inc.");
MODULE_DESCRIPTION("Cadence MHDP PHY driver");
MODULE_LICENSE("GPL v2");

View File

@@ -156,7 +156,6 @@ static int ltq_rcu_usb2_of_parse(struct ltq_rcu_usb2_priv *priv,
{
struct device *dev = priv->dev;
const __be32 *offset;
int ret;
priv->reg_bits = of_device_get_match_data(dev);
@@ -196,10 +195,8 @@ static int ltq_rcu_usb2_of_parse(struct ltq_rcu_usb2_priv *priv,
}
priv->phy_reset = devm_reset_control_get_optional(dev, "phy");
if (IS_ERR(priv->phy_reset))
return PTR_ERR(priv->phy_reset);
return 0;
return PTR_ERR_OR_ZERO(priv->phy_reset);
}
static int ltq_rcu_usb2_phy_probe(struct platform_device *pdev)

View File

@@ -59,3 +59,14 @@ config PHY_PXA_28NM_USB2
The PHY driver will be used by Marvell udc/ehci/otg driver.
To compile this driver as a module, choose M here.
config PHY_PXA_USB
tristate "Marvell PXA USB PHY Driver"
depends on ARCH_PXA || ARCH_MMP
select GENERIC_PHY
help
Enable this to support Marvell PXA USB PHY driver for Marvell
SoC. This driver will do the PHY initialization and shutdown.
The PHY driver will be used by Marvell udc/ehci/otg driver.
To compile this driver as a module, choose M here.

View File

@@ -6,3 +6,4 @@ obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o
obj-$(CONFIG_PHY_PXA_USB) += phy-pxa-usb.o

View File

@@ -231,14 +231,14 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
struct phy_berlin_desc *phy_desc;
if (of_property_read_u32(child, "reg", &phy_id)) {
dev_err(dev, "missing reg property in node %s\n",
child->name);
dev_err(dev, "missing reg property in node %pOFn\n",
child);
ret = -EINVAL;
goto put_child;
}
if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
dev_err(dev, "invalid reg in node %s\n", child->name);
dev_err(dev, "invalid reg in node %pOFn\n", child);
ret = -EINVAL;
goto put_child;
}

View File

@@ -0,0 +1,345 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2011 Marvell International Ltd. All rights reserved.
* Copyright (C) 2018 Lubomir Rintel <lkundrak@v3.sk>
*/
#include <dt-bindings/phy/phy.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
/* phy regs */
#define UTMI_REVISION 0x0
#define UTMI_CTRL 0x4
#define UTMI_PLL 0x8
#define UTMI_TX 0xc
#define UTMI_RX 0x10
#define UTMI_IVREF 0x14
#define UTMI_T0 0x18
#define UTMI_T1 0x1c
#define UTMI_T2 0x20
#define UTMI_T3 0x24
#define UTMI_T4 0x28
#define UTMI_T5 0x2c
#define UTMI_RESERVE 0x30
#define UTMI_USB_INT 0x34
#define UTMI_DBG_CTL 0x38
#define UTMI_OTG_ADDON 0x3c
/* For UTMICTRL Register */
#define UTMI_CTRL_USB_CLK_EN (1 << 31)
/* pxa168 */
#define UTMI_CTRL_SUSPEND_SET1 (1 << 30)
#define UTMI_CTRL_SUSPEND_SET2 (1 << 29)
#define UTMI_CTRL_RXBUF_PDWN (1 << 24)
#define UTMI_CTRL_TXBUF_PDWN (1 << 11)
#define UTMI_CTRL_INPKT_DELAY_SHIFT 30
#define UTMI_CTRL_INPKT_DELAY_SOF_SHIFT 28
#define UTMI_CTRL_PU_REF_SHIFT 20
#define UTMI_CTRL_ARC_PULLDN_SHIFT 12
#define UTMI_CTRL_PLL_PWR_UP_SHIFT 1
#define UTMI_CTRL_PWR_UP_SHIFT 0
/* For UTMI_PLL Register */
#define UTMI_PLL_PLLCALI12_SHIFT 29
#define UTMI_PLL_PLLCALI12_MASK (0x3 << 29)
#define UTMI_PLL_PLLVDD18_SHIFT 27
#define UTMI_PLL_PLLVDD18_MASK (0x3 << 27)
#define UTMI_PLL_PLLVDD12_SHIFT 25
#define UTMI_PLL_PLLVDD12_MASK (0x3 << 25)
#define UTMI_PLL_CLK_BLK_EN_SHIFT 24
#define CLK_BLK_EN (0x1 << 24)
#define PLL_READY (0x1 << 23)
#define KVCO_EXT (0x1 << 22)
#define VCOCAL_START (0x1 << 21)
#define UTMI_PLL_KVCO_SHIFT 15
#define UTMI_PLL_KVCO_MASK (0x7 << 15)
#define UTMI_PLL_ICP_SHIFT 12
#define UTMI_PLL_ICP_MASK (0x7 << 12)
#define UTMI_PLL_FBDIV_SHIFT 4
#define UTMI_PLL_FBDIV_MASK (0xFF << 4)
#define UTMI_PLL_REFDIV_SHIFT 0
#define UTMI_PLL_REFDIV_MASK (0xF << 0)
/* For UTMI_TX Register */
#define UTMI_TX_REG_EXT_FS_RCAL_SHIFT 27
#define UTMI_TX_REG_EXT_FS_RCAL_MASK (0xf << 27)
#define UTMI_TX_REG_EXT_FS_RCAL_EN_SHIFT 26
#define UTMI_TX_REG_EXT_FS_RCAL_EN_MASK (0x1 << 26)
#define UTMI_TX_TXVDD12_SHIFT 22
#define UTMI_TX_TXVDD12_MASK (0x3 << 22)
#define UTMI_TX_CK60_PHSEL_SHIFT 17
#define UTMI_TX_CK60_PHSEL_MASK (0xf << 17)
#define UTMI_TX_IMPCAL_VTH_SHIFT 14
#define UTMI_TX_IMPCAL_VTH_MASK (0x7 << 14)
#define REG_RCAL_START (0x1 << 12)
#define UTMI_TX_LOW_VDD_EN_SHIFT 11
#define UTMI_TX_AMP_SHIFT 0
#define UTMI_TX_AMP_MASK (0x7 << 0)
/* For UTMI_RX Register */
#define UTMI_REG_SQ_LENGTH_SHIFT 15
#define UTMI_REG_SQ_LENGTH_MASK (0x3 << 15)
#define UTMI_RX_SQ_THRESH_SHIFT 4
#define UTMI_RX_SQ_THRESH_MASK (0xf << 4)
#define UTMI_OTG_ADDON_OTG_ON (1 << 0)
enum pxa_usb_phy_version {
PXA_USB_PHY_MMP2,
PXA_USB_PHY_PXA910,
PXA_USB_PHY_PXA168,
};
struct pxa_usb_phy {
struct phy *phy;
void __iomem *base;
enum pxa_usb_phy_version version;
};
/*****************************************************************************
* The registers read/write routines
*****************************************************************************/
static unsigned int u2o_get(void __iomem *base, unsigned int offset)
{
return readl_relaxed(base + offset);
}
static void u2o_set(void __iomem *base, unsigned int offset,
unsigned int value)
{
u32 reg;
reg = readl_relaxed(base + offset);
reg |= value;
writel_relaxed(reg, base + offset);
readl_relaxed(base + offset);
}
static void u2o_clear(void __iomem *base, unsigned int offset,
unsigned int value)
{
u32 reg;
reg = readl_relaxed(base + offset);
reg &= ~value;
writel_relaxed(reg, base + offset);
readl_relaxed(base + offset);
}
static void u2o_write(void __iomem *base, unsigned int offset,
unsigned int value)
{
writel_relaxed(value, base + offset);
readl_relaxed(base + offset);
}
static int pxa_usb_phy_init(struct phy *phy)
{
struct pxa_usb_phy *pxa_usb_phy = phy_get_drvdata(phy);
void __iomem *base = pxa_usb_phy->base;
int loops;
dev_info(&phy->dev, "initializing Marvell PXA USB PHY");
/* Initialize the USB PHY power */
if (pxa_usb_phy->version == PXA_USB_PHY_PXA910) {
u2o_set(base, UTMI_CTRL, (1<<UTMI_CTRL_INPKT_DELAY_SOF_SHIFT)
| (1<<UTMI_CTRL_PU_REF_SHIFT));
}
u2o_set(base, UTMI_CTRL, 1<<UTMI_CTRL_PLL_PWR_UP_SHIFT);
u2o_set(base, UTMI_CTRL, 1<<UTMI_CTRL_PWR_UP_SHIFT);
/* UTMI_PLL settings */
u2o_clear(base, UTMI_PLL, UTMI_PLL_PLLVDD18_MASK
| UTMI_PLL_PLLVDD12_MASK | UTMI_PLL_PLLCALI12_MASK
| UTMI_PLL_FBDIV_MASK | UTMI_PLL_REFDIV_MASK
| UTMI_PLL_ICP_MASK | UTMI_PLL_KVCO_MASK);
u2o_set(base, UTMI_PLL, 0xee<<UTMI_PLL_FBDIV_SHIFT
| 0xb<<UTMI_PLL_REFDIV_SHIFT | 3<<UTMI_PLL_PLLVDD18_SHIFT
| 3<<UTMI_PLL_PLLVDD12_SHIFT | 3<<UTMI_PLL_PLLCALI12_SHIFT
| 1<<UTMI_PLL_ICP_SHIFT | 3<<UTMI_PLL_KVCO_SHIFT);
/* UTMI_TX */
u2o_clear(base, UTMI_TX, UTMI_TX_REG_EXT_FS_RCAL_EN_MASK
| UTMI_TX_TXVDD12_MASK | UTMI_TX_CK60_PHSEL_MASK
| UTMI_TX_IMPCAL_VTH_MASK | UTMI_TX_REG_EXT_FS_RCAL_MASK
| UTMI_TX_AMP_MASK);
u2o_set(base, UTMI_TX, 3<<UTMI_TX_TXVDD12_SHIFT
| 4<<UTMI_TX_CK60_PHSEL_SHIFT | 4<<UTMI_TX_IMPCAL_VTH_SHIFT
| 8<<UTMI_TX_REG_EXT_FS_RCAL_SHIFT | 3<<UTMI_TX_AMP_SHIFT);
/* UTMI_RX */
u2o_clear(base, UTMI_RX, UTMI_RX_SQ_THRESH_MASK
| UTMI_REG_SQ_LENGTH_MASK);
u2o_set(base, UTMI_RX, 7<<UTMI_RX_SQ_THRESH_SHIFT
| 2<<UTMI_REG_SQ_LENGTH_SHIFT);
/* UTMI_IVREF */
if (pxa_usb_phy->version == PXA_USB_PHY_PXA168) {
/*
* fixing Microsoft Altair board interface with NEC hub issue -
* Set UTMI_IVREF from 0x4a3 to 0x4bf
*/
u2o_write(base, UTMI_IVREF, 0x4bf);
}
/* toggle VCOCAL_START bit of UTMI_PLL */
udelay(200);
u2o_set(base, UTMI_PLL, VCOCAL_START);
udelay(40);
u2o_clear(base, UTMI_PLL, VCOCAL_START);
/* toggle REG_RCAL_START bit of UTMI_TX */
udelay(400);
u2o_set(base, UTMI_TX, REG_RCAL_START);
udelay(40);
u2o_clear(base, UTMI_TX, REG_RCAL_START);
udelay(400);
/* Make sure PHY PLL is ready */
loops = 0;
while ((u2o_get(base, UTMI_PLL) & PLL_READY) == 0) {
mdelay(1);
loops++;
if (loops > 100) {
dev_warn(&phy->dev, "calibrate timeout, UTMI_PLL %x\n",
u2o_get(base, UTMI_PLL));
break;
}
}
if (pxa_usb_phy->version == PXA_USB_PHY_PXA168) {
u2o_set(base, UTMI_RESERVE, 1 << 5);
/* Turn on UTMI PHY OTG extension */
u2o_write(base, UTMI_OTG_ADDON, 1);
}
return 0;
}
static int pxa_usb_phy_exit(struct phy *phy)
{
struct pxa_usb_phy *pxa_usb_phy = phy_get_drvdata(phy);
void __iomem *base = pxa_usb_phy->base;
dev_info(&phy->dev, "deinitializing Marvell PXA USB PHY");
if (pxa_usb_phy->version == PXA_USB_PHY_PXA168)
u2o_clear(base, UTMI_OTG_ADDON, UTMI_OTG_ADDON_OTG_ON);
u2o_clear(base, UTMI_CTRL, UTMI_CTRL_RXBUF_PDWN);
u2o_clear(base, UTMI_CTRL, UTMI_CTRL_TXBUF_PDWN);
u2o_clear(base, UTMI_CTRL, UTMI_CTRL_USB_CLK_EN);
u2o_clear(base, UTMI_CTRL, 1<<UTMI_CTRL_PWR_UP_SHIFT);
u2o_clear(base, UTMI_CTRL, 1<<UTMI_CTRL_PLL_PWR_UP_SHIFT);
return 0;
}
static const struct phy_ops pxa_usb_phy_ops = {
.init = pxa_usb_phy_init,
.exit = pxa_usb_phy_exit,
.owner = THIS_MODULE,
};
static const struct of_device_id pxa_usb_phy_of_match[] = {
{
.compatible = "marvell,mmp2-usb-phy",
.data = (void *)PXA_USB_PHY_MMP2,
}, {
.compatible = "marvell,pxa910-usb-phy",
.data = (void *)PXA_USB_PHY_PXA910,
}, {
.compatible = "marvell,pxa168-usb-phy",
.data = (void *)PXA_USB_PHY_PXA168,
},
{ },
};
MODULE_DEVICE_TABLE(of, pxa_usb_phy_of_match);
static int pxa_usb_phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *resource;
struct pxa_usb_phy *pxa_usb_phy;
struct phy_provider *provider;
const struct of_device_id *of_id;
pxa_usb_phy = devm_kzalloc(dev, sizeof(struct pxa_usb_phy), GFP_KERNEL);
if (!pxa_usb_phy)
return -ENOMEM;
of_id = of_match_node(pxa_usb_phy_of_match, dev->of_node);
if (of_id)
pxa_usb_phy->version = (enum pxa_usb_phy_version)of_id->data;
else
pxa_usb_phy->version = PXA_USB_PHY_MMP2;
resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pxa_usb_phy->base = devm_ioremap_resource(dev, resource);
if (IS_ERR(pxa_usb_phy->base)) {
dev_err(dev, "failed to remap PHY regs\n");
return PTR_ERR(pxa_usb_phy->base);
}
pxa_usb_phy->phy = devm_phy_create(dev, NULL, &pxa_usb_phy_ops);
if (IS_ERR(pxa_usb_phy->phy)) {
dev_err(dev, "failed to create PHY\n");
return PTR_ERR(pxa_usb_phy->phy);
}
phy_set_drvdata(pxa_usb_phy->phy, pxa_usb_phy);
provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
if (IS_ERR(provider)) {
dev_err(dev, "failed to register PHY provider\n");
return PTR_ERR(provider);
}
if (!dev->of_node) {
phy_create_lookup(pxa_usb_phy->phy, "usb", "mv-udc");
phy_create_lookup(pxa_usb_phy->phy, "usb", "pxa-u2oehci");
phy_create_lookup(pxa_usb_phy->phy, "usb", "mv-otg");
}
dev_info(dev, "Marvell PXA USB PHY");
return 0;
}
static struct platform_driver pxa_usb_phy_driver = {
.probe = pxa_usb_phy_probe,
.driver = {
.name = "pxa-usb-phy",
.of_match_table = pxa_usb_phy_of_match,
},
};
module_platform_driver(pxa_usb_phy_driver);
MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
MODULE_DESCRIPTION("Marvell PXA USB PHY Driver");
MODULE_LICENSE("GPL v2");

View File

@@ -50,6 +50,23 @@ config PHY_QCOM_UFS
help
Support for UFS PHY on QCOM chipsets.
if PHY_QCOM_UFS
config PHY_QCOM_UFS_14NM
tristate
default PHY_QCOM_UFS
help
Support for 14nm UFS QMP phy present on QCOM chipsets.
config PHY_QCOM_UFS_20NM
tristate
default PHY_QCOM_UFS
depends on BROKEN
help
Support for 20nm UFS QMP phy present on QCOM chipsets.
endif
config PHY_QCOM_USB_HS
tristate "Qualcomm USB HS PHY module"
depends on USB_ULPI_BUS

View File

@@ -5,7 +5,7 @@ obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_UFS_14NM) += phy-qcom-ufs-qmp-14nm.o
obj-$(CONFIG_PHY_QCOM_UFS_20NM) += phy-qcom-ufs-qmp-20nm.o
obj-$(CONFIG_PHY_QCOM_USB_HS) += phy-qcom-usb-hs.o
obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o

View File

@@ -156,6 +156,11 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
[QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
};
static const unsigned int sdm845_ufsphy_regs_layout[] = {
[QPHY_START_CTRL] = 0x00,
[QPHY_PCS_READY_STATUS] = 0x160,
};
static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
@@ -601,6 +606,83 @@ static const struct qmp_phy_init_tbl qmp_v3_usb3_uniphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG2, 0x60),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x0a),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0xd5),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_CTRL, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x05),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL1, 0xff),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_INITVAL2, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xda),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0xff),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0c),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE1, 0x98),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE1, 0x16),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE1, 0x36),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE1, 0x3f),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE1, 0xc1),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE1, 0x00),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE1, 0x32),
QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE1, 0x0f),
/* Rate B */
QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x44),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_tx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_RX, 0x07),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_rx_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_LVL, 0x24),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x0f),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x1e),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_TERM_BW, 0x5b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x06),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SVS_SO_GAIN, 0x04),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x81),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x80),
QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
};
static const struct qmp_phy_init_tbl sdm845_ufsphy_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL2, 0x6e),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL, 0x0a),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL, 0x02),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SYM_RESYNC_CTRL, 0x03),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_TX_MID_TERM_CTRL1, 0x43),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_CTRL1, 0x0f),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_MIN_HIBERN8_TIME, 0x9a),
QMP_PHY_INIT_CFG(QPHY_V3_PCS_MULTI_LANE_CTRL1, 0x02),
};
/* struct qmp_phy_cfg - per-PHY initialization config */
struct qmp_phy_cfg {
@@ -649,9 +731,14 @@ struct qmp_phy_cfg {
/* true, if PHY has a separate DP_COM control block */
bool has_phy_dp_com_ctrl;
/* true, if PHY has secondary tx/rx lanes to be configured */
bool is_dual_lane_phy;
/* Register offset of secondary tx/rx lanes for USB DP combo PHY */
unsigned int tx_b_lane_offset;
unsigned int rx_b_lane_offset;
/* true, if PCS block has no separate SW_RESET register */
bool no_pcs_sw_reset;
};
/**
@@ -748,6 +835,10 @@ static const char * const qmp_v3_phy_clk_l[] = {
"aux", "cfg_ahb", "ref", "com_aux",
};
static const char * const sdm845_ufs_phy_clk_l[] = {
"ref", "ref_aux",
};
/* list of resets */
static const char * const msm8996_pciephy_reset_l[] = {
"phy", "common", "cfg",
@@ -758,7 +849,7 @@ static const char * const msm8996_usb3phy_reset_l[] = {
};
/* list of regulators */
static const char * const msm8996_phy_vreg_l[] = {
static const char * const qmp_phy_vreg_l[] = {
"vdda-phy", "vdda-pll",
};
@@ -778,8 +869,8 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
.num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
.reset_list = msm8996_pciephy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = pciephy_regs_layout,
.start_ctrl = PCS_START | PLL_READY_GATE_EN,
@@ -809,8 +900,8 @@ static const struct qmp_phy_cfg msm8996_usb3phy_cfg = {
.num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
@@ -870,8 +961,8 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
.num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = qmp_v3_usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
@@ -883,6 +974,7 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
.has_phy_dp_com_ctrl = true,
.is_dual_lane_phy = true,
.tx_b_lane_offset = 0x400,
.rx_b_lane_offset = 0x400,
};
@@ -903,8 +995,8 @@ static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
.num_clks = ARRAY_SIZE(qmp_v3_phy_clk_l),
.reset_list = msm8996_usb3phy_reset_l,
.num_resets = ARRAY_SIZE(msm8996_usb3phy_reset_l),
.vreg_list = msm8996_phy_vreg_l,
.num_vregs = ARRAY_SIZE(msm8996_phy_vreg_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = qmp_v3_usb3phy_regs_layout,
.start_ctrl = SERDES_START | PCS_START,
@@ -916,6 +1008,35 @@ static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
.pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX,
};
static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
.type = PHY_TYPE_UFS,
.nlanes = 2,
.serdes_tbl = sdm845_ufsphy_serdes_tbl,
.serdes_tbl_num = ARRAY_SIZE(sdm845_ufsphy_serdes_tbl),
.tx_tbl = sdm845_ufsphy_tx_tbl,
.tx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_tx_tbl),
.rx_tbl = sdm845_ufsphy_rx_tbl,
.rx_tbl_num = ARRAY_SIZE(sdm845_ufsphy_rx_tbl),
.pcs_tbl = sdm845_ufsphy_pcs_tbl,
.pcs_tbl_num = ARRAY_SIZE(sdm845_ufsphy_pcs_tbl),
.clk_list = sdm845_ufs_phy_clk_l,
.num_clks = ARRAY_SIZE(sdm845_ufs_phy_clk_l),
.vreg_list = qmp_phy_vreg_l,
.num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
.regs = sdm845_ufsphy_regs_layout,
.start_ctrl = SERDES_START,
.pwrdn_ctrl = SW_PWRDN,
.mask_pcs_ready = PCS_READY,
.is_dual_lane_phy = true,
.tx_b_lane_offset = 0x400,
.rx_b_lane_offset = 0x400,
.no_pcs_sw_reset = true,
};
static void qcom_qmp_phy_configure(void __iomem *base,
const unsigned int *regs,
const struct qmp_phy_init_tbl tbl[],
@@ -935,10 +1056,12 @@ static void qcom_qmp_phy_configure(void __iomem *base,
}
}
static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
{
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *serdes = qmp->serdes;
void __iomem *pcs = qphy->pcs;
void __iomem *dp_com = qmp->dp_com;
int ret, i;
@@ -979,10 +1102,6 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
goto err_rst;
}
if (cfg->has_phy_com_ctrl)
qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
SW_PWRDN);
if (cfg->has_phy_dp_com_ctrl) {
qphy_setbits(dp_com, QPHY_V3_DP_COM_POWER_DOWN_CTRL,
SW_PWRDN);
@@ -1000,6 +1119,12 @@ static int qcom_qmp_phy_com_init(struct qcom_qmp *qmp)
SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
}
if (cfg->has_phy_com_ctrl)
qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
SW_PWRDN);
else
qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
/* Serdes configuration */
qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
cfg->serdes_tbl_num);
@@ -1090,7 +1215,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
dev_vdbg(qmp->dev, "Initializing QMP phy\n");
ret = qcom_qmp_phy_com_init(qmp);
ret = qcom_qmp_phy_com_init(qphy);
if (ret)
return ret;
@@ -1112,22 +1237,31 @@ static int qcom_qmp_phy_init(struct phy *phy)
/* Tx, Rx, and PCS configurations */
qcom_qmp_phy_configure(tx, cfg->regs, cfg->tx_tbl, cfg->tx_tbl_num);
/* Configuration for other LANE for USB-DP combo PHY */
if (cfg->has_phy_dp_com_ctrl)
if (cfg->is_dual_lane_phy)
qcom_qmp_phy_configure(tx + cfg->tx_b_lane_offset, cfg->regs,
cfg->tx_tbl, cfg->tx_tbl_num);
qcom_qmp_phy_configure(rx, cfg->regs, cfg->rx_tbl, cfg->rx_tbl_num);
if (cfg->has_phy_dp_com_ctrl)
if (cfg->is_dual_lane_phy)
qcom_qmp_phy_configure(rx + cfg->rx_b_lane_offset, cfg->regs,
cfg->rx_tbl, cfg->rx_tbl_num);
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
/*
* UFS PHY requires the deassert of software reset before serdes start.
* For UFS PHYs that do not have software reset control bits, defer
* starting serdes until the power on callback.
*/
if ((cfg->type == PHY_TYPE_UFS) && cfg->no_pcs_sw_reset)
goto out;
/*
* Pull out PHY from POWER DOWN state.
* This is active low enable signal to power-down PHY.
*/
qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
if(cfg->type == PHY_TYPE_PCIE)
qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
if (cfg->has_pwrdn_delay)
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
@@ -1151,6 +1285,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
}
qmp->phy_initialized = true;
out:
return ret;
err_pcs_ready:
@@ -1173,7 +1308,8 @@ static int qcom_qmp_phy_exit(struct phy *phy)
clk_disable_unprepare(qphy->pipe_clk);
/* PHY reset */
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
if (!cfg->no_pcs_sw_reset)
qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
/* stop SerDes and Phy-Coding-Sublayer */
qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
@@ -1191,6 +1327,44 @@ static int qcom_qmp_phy_exit(struct phy *phy)
return 0;
}
static int qcom_qmp_phy_poweron(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
const struct qmp_phy_cfg *cfg = qmp->cfg;
void __iomem *pcs = qphy->pcs;
void __iomem *status;
unsigned int mask, val;
int ret = 0;
if (cfg->type != PHY_TYPE_UFS)
return 0;
/*
* For UFS PHY that has not software reset control, serdes start
* should only happen when UFS driver explicitly calls phy_power_on
* after it deasserts software reset.
*/
if (cfg->no_pcs_sw_reset && !qmp->phy_initialized &&
(qmp->init_count != 0)) {
/* start SerDes and Phy-Coding-Sublayer */
qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
mask = cfg->mask_pcs_ready;
ret = readl_poll_timeout(status, val, !(val & mask), 1,
PHY_INIT_COMPLETE_TIMEOUT);
if (ret) {
dev_err(qmp->dev, "phy initialization timed-out\n");
return ret;
}
qmp->phy_initialized = true;
}
return ret;
}
static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
@@ -1400,7 +1574,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
ret = of_property_read_string(np, "clock-output-names", &init.name);
if (ret) {
dev_err(qmp->dev, "%s: No clock-output-names\n", np->name);
dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
return ret;
}
@@ -1420,6 +1594,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
static const struct phy_ops qcom_qmp_phy_gen_ops = {
.init = qcom_qmp_phy_init,
.exit = qcom_qmp_phy_exit,
.power_on = qcom_qmp_phy_poweron,
.set_mode = qcom_qmp_phy_set_mode,
.owner = THIS_MODULE,
};
@@ -1522,6 +1697,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
}, {
.compatible = "qcom,sdm845-qmp-usb3-uni-phy",
.data = &qmp_v3_usb3_uniphy_cfg,
}, {
.compatible = "qcom,sdm845-qmp-ufs-phy",
.data = &sdm845_ufsphy_cfg,
},
{ },
};
@@ -1586,7 +1764,9 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
ret = qcom_qmp_phy_vreg_init(dev);
if (ret) {
dev_err(dev, "failed to get regulator supplies\n");
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get regulator supplies: %d\n",
ret);
return ret;
}

View File

@@ -184,6 +184,8 @@
#define QSERDES_V3_COM_VCO_TUNE2_MODE0 0x0f8
#define QSERDES_V3_COM_VCO_TUNE1_MODE1 0x0fc
#define QSERDES_V3_COM_VCO_TUNE2_MODE1 0x100
#define QSERDES_V3_COM_VCO_TUNE_INITVAL1 0x104
#define QSERDES_V3_COM_VCO_TUNE_INITVAL2 0x108
#define QSERDES_V3_COM_VCO_TUNE_TIMER1 0x11c
#define QSERDES_V3_COM_VCO_TUNE_TIMER2 0x120
#define QSERDES_V3_COM_CLK_SELECT 0x138
@@ -211,8 +213,13 @@
/* Only for QMP V3 PHY - RX registers */
#define QSERDES_V3_RX_UCDR_SO_GAIN_HALF 0x00c
#define QSERDES_V3_RX_UCDR_SO_GAIN 0x014
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_HALF 0x024
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN_QUARTER 0x028
#define QSERDES_V3_RX_UCDR_SVS_SO_GAIN 0x02c
#define QSERDES_V3_RX_UCDR_FASTLOCK_FO_GAIN 0x030
#define QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE 0x034
#define QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW 0x03c
#define QSERDES_V3_RX_UCDR_PI_CONTROLS 0x044
#define QSERDES_V3_RX_RX_TERM_BW 0x07c
#define QSERDES_V3_RX_VGA_CAL_CNTRL1 0x0bc
#define QSERDES_V3_RX_VGA_CAL_CNTRL2 0x0c0
@@ -239,6 +246,8 @@
#define QPHY_V3_PCS_TXMGN_V3 0x018
#define QPHY_V3_PCS_TXMGN_V4 0x01c
#define QPHY_V3_PCS_TXMGN_LS 0x020
#define QPHY_V3_PCS_TX_LARGE_AMP_DRV_LVL 0x02c
#define QPHY_V3_PCS_TX_SMALL_AMP_DRV_LVL 0x034
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V0 0x024
#define QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0 0x028
#define QPHY_V3_PCS_TXDEEMPH_M6DB_V1 0x02c
@@ -275,6 +284,12 @@
#define QPHY_V3_PCS_FLL_CNT_VAL_L 0x0cc
#define QPHY_V3_PCS_FLL_CNT_VAL_H_TOL 0x0d0
#define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4
#define QPHY_V3_PCS_RX_SYM_RESYNC_CTRL 0x134
#define QPHY_V3_PCS_RX_MIN_HIBERN8_TIME 0x138
#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c
#define QPHY_V3_PCS_RX_SIGDET_CTRL2 0x140
#define QPHY_V3_PCS_TX_MID_TERM_CTRL1 0x1bc
#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4
#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210

View File

@@ -800,7 +800,9 @@ static int qusb2_phy_probe(struct platform_device *pdev)
ret = devm_regulator_bulk_get(dev, num, qphy->vregs);
if (ret) {
dev_err(dev, "failed to get regulator supplies\n");
if (ret != -EPROBE_DEFER)
dev_err(dev, "failed to get regulator supplies: %d\n",
ret);
return ret;
}

View File

@@ -17,9 +17,9 @@
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/phy/phy-qcom-ufs.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/delay.h>

View File

@@ -431,56 +431,6 @@ static void ufs_qcom_phy_disable_ref_clk(struct ufs_qcom_phy *phy)
}
}
#define UFS_REF_CLK_EN (1 << 5)
static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable)
{
struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
if (phy->dev_ref_clk_ctrl_mmio &&
(enable ^ phy->is_dev_ref_clk_enabled)) {
u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio);
if (enable)
temp |= UFS_REF_CLK_EN;
else
temp &= ~UFS_REF_CLK_EN;
/*
* If we are here to disable this clock immediately after
* entering into hibern8, we need to make sure that device
* ref_clk is active atleast 1us after the hibern8 enter.
*/
if (!enable)
udelay(1);
writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio);
/* ensure that ref_clk is enabled/disabled before we return */
wmb();
/*
* If we call hibern8 exit after this, we need to make sure that
* device ref_clk is stable for atleast 1us before the hibern8
* exit command.
*/
if (enable)
udelay(1);
phy->is_dev_ref_clk_enabled = enable;
}
}
void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy)
{
ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true);
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_enable_dev_ref_clk);
void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy)
{
ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false);
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_disable_dev_ref_clk);
/* Turn ON M-PHY RMMI interface clocks */
static int ufs_qcom_phy_enable_iface_clk(struct ufs_qcom_phy *phy)
{

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
#
# Phy drivers for Renesas platforms
#

View File

@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
obj-$(CONFIG_PHY_RCAR_GEN3_PCIE) += phy-rcar-gen3-pcie.o
obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o

View File

@@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car Gen2 PHY driver
*
* Copyright (C) 2014 Renesas Solutions Corp.
* Copyright (C) 2014 Cogent Embedded, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>

View File

@@ -1,3 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car Gen3 for USB2.0 PHY driver
*
@@ -6,10 +7,6 @@
* This is based on the phy-rcar-gen2 driver:
* Copyright (C) 2014 Renesas Solutions Corp.
* Copyright (C) 2014 Cogent Embedded, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/extcon-provider.h>
@@ -81,18 +78,29 @@
#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
#define USB2_ADPCTRL_DRVVBUS BIT(4)
#define RCAR_GEN3_PHY_HAS_DEDICATED_PINS 1
struct rcar_gen3_chan {
void __iomem *base;
struct extcon_dev *extcon;
struct phy *phy;
struct regulator *vbus;
struct work_struct work;
enum usb_dr_mode dr_mode;
bool extcon_host;
bool has_otg_pins;
bool is_otg_channel;
bool uses_otg_pins;
};
/*
* Combination about is_otg_channel and uses_otg_pins:
*
* Parameters || Behaviors
* is_otg_channel | uses_otg_pins || irqs | role sysfs
* ---------------------+---------------++--------------+------------
* true | true || enabled | enabled
* true | false || disabled | enabled
* false | any || disabled | disabled
*/
static void rcar_gen3_phy_usb2_work(struct work_struct *work)
{
struct rcar_gen3_chan *ch = container_of(work, struct rcar_gen3_chan,
@@ -147,6 +155,18 @@ static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus)
writel(val, usb2_base + USB2_ADPCTRL);
}
static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable)
{
void __iomem *usb2_base = ch->base;
u32 val = readl(usb2_base + USB2_OBINTEN);
if (ch->uses_otg_pins && enable)
val |= USB2_OBINT_BITS;
else
val &= ~USB2_OBINT_BITS;
writel(val, usb2_base + USB2_OBINTEN);
}
static void rcar_gen3_init_for_host(struct rcar_gen3_chan *ch)
{
rcar_gen3_set_linectrl(ch, 1, 1);
@@ -192,20 +212,19 @@ static void rcar_gen3_init_for_a_peri(struct rcar_gen3_chan *ch)
static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch)
{
void __iomem *usb2_base = ch->base;
u32 val;
rcar_gen3_control_otg_irq(ch, 0);
val = readl(usb2_base + USB2_OBINTEN);
writel(val & ~USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
rcar_gen3_enable_vbus_ctrl(ch, 0);
rcar_gen3_enable_vbus_ctrl(ch, 1);
rcar_gen3_init_for_host(ch);
writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
rcar_gen3_control_otg_irq(ch, 1);
}
static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch)
{
if (!ch->uses_otg_pins)
return (ch->dr_mode == USB_DR_MODE_HOST) ? false : true;
return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG);
}
@@ -237,7 +256,7 @@ static ssize_t role_store(struct device *dev, struct device_attribute *attr,
bool is_b_device;
enum phy_mode cur_mode, new_mode;
if (!ch->has_otg_pins || !ch->phy->init_count)
if (!ch->is_otg_channel || !ch->phy->init_count)
return -EIO;
if (!strncmp(buf, "host", strlen("host")))
@@ -275,7 +294,7 @@ static ssize_t role_show(struct device *dev, struct device_attribute *attr,
{
struct rcar_gen3_chan *ch = dev_get_drvdata(dev);
if (!ch->has_otg_pins || !ch->phy->init_count)
if (!ch->is_otg_channel || !ch->phy->init_count)
return -EIO;
return sprintf(buf, "%s\n", rcar_gen3_is_host(ch) ? "host" :
@@ -291,8 +310,7 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch)
val = readl(usb2_base + USB2_VBCTRL);
writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL);
writel(USB2_OBINT_BITS, usb2_base + USB2_OBINTSTA);
val = readl(usb2_base + USB2_OBINTEN);
writel(val | USB2_OBINT_BITS, usb2_base + USB2_OBINTEN);
rcar_gen3_control_otg_irq(ch, 1);
val = readl(usb2_base + USB2_ADPCTRL);
writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL);
val = readl(usb2_base + USB2_LINECTRL1);
@@ -314,7 +332,7 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
/* Initialize otg part */
if (channel->has_otg_pins)
if (channel->is_otg_channel)
rcar_gen3_init_otg(channel);
return 0;
@@ -388,21 +406,10 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
}
static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
{
.compatible = "renesas,usb2-phy-r8a7795",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,usb2-phy-r8a7796",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,usb2-phy-r8a77965",
.data = (void *)RCAR_GEN3_PHY_HAS_DEDICATED_PINS,
},
{
.compatible = "renesas,rcar-gen3-usb2-phy",
},
{ .compatible = "renesas,usb2-phy-r8a7795" },
{ .compatible = "renesas,usb2-phy-r8a7796" },
{ .compatible = "renesas,usb2-phy-r8a77965" },
{ .compatible = "renesas,rcar-gen3-usb2-phy" },
{ }
};
MODULE_DEVICE_TABLE(of, rcar_gen3_phy_usb2_match_table);
@@ -445,10 +452,13 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
dev_err(dev, "No irq handler (%d)\n", irq);
}
if (of_usb_get_dr_mode_by_phy(dev->of_node, 0) == USB_DR_MODE_OTG) {
channel->dr_mode = of_usb_get_dr_mode_by_phy(dev->of_node, 0);
if (channel->dr_mode != USB_DR_MODE_UNKNOWN) {
int ret;
channel->has_otg_pins = (uintptr_t)of_device_get_match_data(dev);
channel->is_otg_channel = true;
channel->uses_otg_pins = !of_property_read_bool(dev->of_node,
"renesas,no-otg-pins");
channel->extcon = devm_extcon_dev_allocate(dev,
rcar_gen3_phy_cable);
if (IS_ERR(channel->extcon))
@@ -490,7 +500,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
dev_err(dev, "Failed to register PHY provider\n");
ret = PTR_ERR(provider);
goto error;
} else if (channel->has_otg_pins) {
} else if (channel->is_otg_channel) {
int ret;
ret = device_create_file(dev, &dev_attr_role);
@@ -510,7 +520,7 @@ static int rcar_gen3_phy_usb2_remove(struct platform_device *pdev)
{
struct rcar_gen3_chan *channel = platform_get_drvdata(pdev);
if (channel->has_otg_pins)
if (channel->is_otg_channel)
device_remove_file(&pdev->dev, &dev_attr_role);
pm_runtime_disable(&pdev->dev);

View File

@@ -1,11 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas R-Car Gen3 for USB3.0 PHY driver
*
* Copyright (C) 2017 Renesas Electronics Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>

View File

@@ -15,6 +15,14 @@ config PHY_ROCKCHIP_EMMC
help
Enable this to support the Rockchip EMMC PHY.
config PHY_ROCKCHIP_INNO_HDMI
tristate "Rockchip INNO HDMI PHY Driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
depends on COMMON_CLK
select GENERIC_PHY
help
Enable this to support the Rockchip Innosilicon HDMI PHY.
config PHY_ROCKCHIP_INNO_USB2
tristate "Rockchip INNO USB2PHY Driver"
depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF

View File

@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o

View File

@@ -337,8 +337,8 @@ static int rockchip_emmc_phy_probe(struct platform_device *pdev)
return -ENOMEM;
if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
dev_err(dev, "missing reg property in node %s\n",
dev->of_node->name);
dev_err(dev, "missing reg property in node %pOFn\n",
dev->of_node);
return -EINVAL;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1116,8 +1116,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
}
if (of_property_read_u32(np, "reg", &reg)) {
dev_err(dev, "the reg property is not assigned in %s node\n",
np->name);
dev_err(dev, "the reg property is not assigned in %pOFn node\n",
np);
return -EINVAL;
}
@@ -1143,8 +1143,8 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev)
}
if (!rphy->phy_cfg) {
dev_err(dev, "no phy-config can be matched with %s node\n",
np->name);
dev_err(dev, "no phy-config can be matched with %pOFn node\n",
np);
return -EINVAL;
}

View File

@@ -1145,8 +1145,8 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
}
if (!tcphy->port_cfgs) {
dev_err(dev, "no phy-config can be matched with %s node\n",
np->name);
dev_err(dev, "no phy-config can be matched with %pOFn node\n",
np);
return -EINVAL;
}
@@ -1186,8 +1186,8 @@ static int rockchip_typec_phy_probe(struct platform_device *pdev)
continue;
if (IS_ERR(phy)) {
dev_err(dev, "failed to create phy: %s\n",
child_np->name);
dev_err(dev, "failed to create phy: %pOFn\n",
child_np);
pm_runtime_disable(dev);
return PTR_ERR(phy);
}

View File

@@ -36,7 +36,22 @@ static int enable_usb_uart;
#define HIWORD_UPDATE(val, mask) \
((val) | (mask) << 16)
#define UOC_CON0_SIDDQ BIT(13)
#define UOC_CON0 0x00
#define UOC_CON0_SIDDQ BIT(13)
#define UOC_CON0_DISABLE BIT(4)
#define UOC_CON0_COMMON_ON_N BIT(0)
#define UOC_CON2 0x08
#define UOC_CON2_SOFT_CON_SEL BIT(2)
#define UOC_CON3 0x0c
/* bits present on rk3188 and rk3288 phys */
#define UOC_CON3_UTMI_TERMSEL_FULLSPEED BIT(5)
#define UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3)
#define UOC_CON3_UTMI_XCVRSEELCT_MASK (3 << 3)
#define UOC_CON3_UTMI_OPMODE_NODRIVING (1 << 1)
#define UOC_CON3_UTMI_OPMODE_MASK (3 << 1)
#define UOC_CON3_UTMI_SUSPENDN BIT(0)
struct rockchip_usb_phys {
int reg;
@@ -46,7 +61,8 @@ struct rockchip_usb_phys {
struct rockchip_usb_phy_base;
struct rockchip_usb_phy_pdata {
struct rockchip_usb_phys *phys;
int (*init_usb_uart)(struct regmap *grf);
int (*init_usb_uart)(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata);
int usb_uart_phy;
};
@@ -208,8 +224,8 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
rk_phy->np = child;
if (of_property_read_u32(child, "reg", &reg_offset)) {
dev_err(base->dev, "missing reg property in node %s\n",
child->name);
dev_err(base->dev, "missing reg property in node %pOFn\n",
child);
return -EINVAL;
}
@@ -313,28 +329,88 @@ static const struct rockchip_usb_phy_pdata rk3066a_pdata = {
},
};
static int __init rockchip_init_usb_uart_common(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
int regoffs = pdata->phys[pdata->usb_uart_phy].reg;
int ret;
u32 val;
/*
* COMMON_ON and DISABLE settings are described in the TRM,
* but were not present in the original code.
* Also disable the analog phy components to save power.
*/
val = HIWORD_UPDATE(UOC_CON0_COMMON_ON_N
| UOC_CON0_DISABLE
| UOC_CON0_SIDDQ,
UOC_CON0_COMMON_ON_N
| UOC_CON0_DISABLE
| UOC_CON0_SIDDQ);
ret = regmap_write(grf, regoffs + UOC_CON0, val);
if (ret)
return ret;
val = HIWORD_UPDATE(UOC_CON2_SOFT_CON_SEL,
UOC_CON2_SOFT_CON_SEL);
ret = regmap_write(grf, regoffs + UOC_CON2, val);
if (ret)
return ret;
val = HIWORD_UPDATE(UOC_CON3_UTMI_OPMODE_NODRIVING
| UOC_CON3_UTMI_XCVRSEELCT_FSTRANSC
| UOC_CON3_UTMI_TERMSEL_FULLSPEED,
UOC_CON3_UTMI_SUSPENDN
| UOC_CON3_UTMI_OPMODE_MASK
| UOC_CON3_UTMI_XCVRSEELCT_MASK
| UOC_CON3_UTMI_TERMSEL_FULLSPEED);
ret = regmap_write(grf, UOC_CON3, val);
if (ret)
return ret;
return 0;
}
#define RK3188_UOC0_CON0 0x10c
#define RK3188_UOC0_CON0_BYPASSSEL BIT(9)
#define RK3188_UOC0_CON0_BYPASSDMEN BIT(8)
/*
* Enable the bypass of uart2 data through the otg usb phy.
* See description of rk3288-variant for details.
*/
static int __init rk3188_init_usb_uart(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
u32 val;
int ret;
ret = rockchip_init_usb_uart_common(grf, pdata);
if (ret)
return ret;
val = HIWORD_UPDATE(RK3188_UOC0_CON0_BYPASSSEL
| RK3188_UOC0_CON0_BYPASSDMEN,
RK3188_UOC0_CON0_BYPASSSEL
| RK3188_UOC0_CON0_BYPASSDMEN);
ret = regmap_write(grf, RK3188_UOC0_CON0, val);
if (ret)
return ret;
return 0;
}
static const struct rockchip_usb_phy_pdata rk3188_pdata = {
.phys = (struct rockchip_usb_phys[]){
{ .reg = 0x10c, .pll_name = "sclk_otgphy0_480m" },
{ .reg = 0x11c, .pll_name = "sclk_otgphy1_480m" },
{ /* sentinel */ }
},
.init_usb_uart = rk3188_init_usb_uart,
.usb_uart_phy = 0,
};
#define RK3288_UOC0_CON0 0x320
#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0)
#define RK3288_UOC0_CON0_DISABLE BIT(4)
#define RK3288_UOC0_CON2 0x328
#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2)
#define RK3288_UOC0_CON3 0x32c
#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0)
#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1)
#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1)
#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3)
#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3)
#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5)
#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6)
#define RK3288_UOC0_CON3_BYPASSSEL BIT(7)
@@ -353,40 +429,13 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata = {
*
* The actual code in the vendor kernel does some things differently.
*/
static int __init rk3288_init_usb_uart(struct regmap *grf)
static int __init rk3288_init_usb_uart(struct regmap *grf,
const struct rockchip_usb_phy_pdata *pdata)
{
u32 val;
int ret;
/*
* COMMON_ON and DISABLE settings are described in the TRM,
* but were not present in the original code.
* Also disable the analog phy components to save power.
*/
val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N
| RK3288_UOC0_CON0_DISABLE
| UOC_CON0_SIDDQ,
RK3288_UOC0_CON0_COMMON_ON_N
| RK3288_UOC0_CON0_DISABLE
| UOC_CON0_SIDDQ);
ret = regmap_write(grf, RK3288_UOC0_CON0, val);
if (ret)
return ret;
val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL,
RK3288_UOC0_CON2_SOFT_CON_SEL);
ret = regmap_write(grf, RK3288_UOC0_CON2, val);
if (ret)
return ret;
val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING
| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC
| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED,
RK3288_UOC0_CON3_UTMI_SUSPENDN
| RK3288_UOC0_CON3_UTMI_OPMODE_MASK
| RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK
| RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED);
ret = regmap_write(grf, RK3288_UOC0_CON3, val);
ret = rockchip_init_usb_uart_common(grf, pdata);
if (ret)
return ret;
@@ -516,7 +565,7 @@ static int __init rockchip_init_usb_uart(void)
return PTR_ERR(grf);
}
ret = data->init_usb_uart(grf);
ret = data->init_usb_uart(grf, data);
if (ret) {
pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
enable_usb_uart = 0;

View File

@@ -0,0 +1,34 @@
#
# PHY drivers for Socionext platforms.
#
config PHY_UNIPHIER_USB2
tristate "UniPhier USB2 PHY driver"
depends on ARCH_UNIPHIER || COMPILE_TEST
depends on OF && HAS_IOMEM
select GENERIC_PHY
select MFD_SYSCON
help
Enable this to support USB PHY implemented on USB2 controller
on UniPhier SoCs. This driver provides interface to interact
with USB 2.0 PHY that is part of the UniPhier SoC.
In case of Pro4, it is necessary to specify this USB2 PHY instead
of USB3 HS-PHY.
config PHY_UNIPHIER_USB3
tristate "UniPhier USB3 PHY driver"
depends on ARCH_UNIPHIER || COMPILE_TEST
depends on OF && HAS_IOMEM
select GENERIC_PHY
help
Enable this to support USB PHY implemented in USB3 controller
on UniPhier SoCs. This controller supports USB3.0 and lower speed.
config PHY_UNIPHIER_PCIE
tristate "Uniphier PHY driver for PCIe controller"
depends on (ARCH_UNIPHIER || COMPILE_TEST) && OF
default PCIE_UNIPHIER
select GENERIC_PHY
help
Enable this to support PHY implemented in PCIe controller
on UniPhier SoCs. This driver supports LD20 and PXs3 SoCs.

View File

@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the phy drivers.
#
obj-$(CONFIG_PHY_UNIPHIER_USB2) += phy-uniphier-usb2.o
obj-$(CONFIG_PHY_UNIPHIER_USB3) += phy-uniphier-usb3hs.o phy-uniphier-usb3ss.o
obj-$(CONFIG_PHY_UNIPHIER_PCIE) += phy-uniphier-pcie.o

View File

@@ -0,0 +1,240 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-pcie.c - PHY driver for UniPhier PCIe controller
* Copyright 2018, Socionext Inc.
* Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/resource.h>
/* PHY */
#define PCL_PHY_TEST_I 0x2000
#define PCL_PHY_TEST_O 0x2004
#define TESTI_DAT_MASK GENMASK(13, 6)
#define TESTI_ADR_MASK GENMASK(5, 1)
#define TESTI_WR_EN BIT(0)
#define PCL_PHY_RESET 0x200c
#define PCL_PHY_RESET_N_MNMODE BIT(8) /* =1:manual */
#define PCL_PHY_RESET_N BIT(0) /* =1:deasssert */
/* SG */
#define SG_USBPCIESEL 0x590
#define SG_USBPCIESEL_PCIE BIT(0)
#define PCL_PHY_R00 0
#define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */
#define PCL_PHY_R06 6
#define RX_EQ_ADJ GENMASK(5, 0) /* EQ adjustment value */
#define RX_EQ_ADJ_VAL 0
#define PCL_PHY_R26 26
#define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */
#define VCO_CTRL_INIT_VAL 5
struct uniphier_pciephy_priv {
void __iomem *base;
struct device *dev;
struct clk *clk;
struct reset_control *rst;
const struct uniphier_pciephy_soc_data *data;
};
struct uniphier_pciephy_soc_data {
bool has_syscon;
};
static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv,
u32 data)
{
/* need to read TESTO twice after accessing TESTI */
writel(data, priv->base + PCL_PHY_TEST_I);
readl(priv->base + PCL_PHY_TEST_O);
readl(priv->base + PCL_PHY_TEST_O);
}
static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv,
u32 reg, u32 mask, u32 param)
{
u32 val;
/* read previous data */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
val = readl(priv->base + PCL_PHY_TEST_O);
/* update value */
val &= ~FIELD_PREP(TESTI_DAT_MASK, mask);
val = FIELD_PREP(TESTI_DAT_MASK, mask & param);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN);
uniphier_pciephy_testio_write(priv, val);
/* read current data as dummy */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, reg);
uniphier_pciephy_testio_write(priv, val);
readl(priv->base + PCL_PHY_TEST_O);
}
static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv)
{
u32 val;
val = readl(priv->base + PCL_PHY_RESET);
val &= ~PCL_PHY_RESET_N;
val |= PCL_PHY_RESET_N_MNMODE;
writel(val, priv->base + PCL_PHY_RESET);
}
static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv)
{
u32 val;
val = readl(priv->base + PCL_PHY_RESET);
val |= PCL_PHY_RESET_N_MNMODE | PCL_PHY_RESET_N;
writel(val, priv->base + PCL_PHY_RESET);
}
static int uniphier_pciephy_init(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
return ret;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_disable;
uniphier_pciephy_set_param(priv, PCL_PHY_R00,
RX_EQ_ADJ_EN, RX_EQ_ADJ_EN);
uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ,
FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL));
uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL,
FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL));
usleep_range(1, 10);
uniphier_pciephy_deassert(priv);
usleep_range(1, 10);
return 0;
out_clk_disable:
clk_disable_unprepare(priv->clk);
return ret;
}
static int uniphier_pciephy_exit(struct phy *phy)
{
struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
uniphier_pciephy_assert(priv);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
return 0;
}
static const struct phy_ops uniphier_pciephy_ops = {
.init = uniphier_pciephy_init,
.exit = uniphier_pciephy_exit,
.owner = THIS_MODULE,
};
static int uniphier_pciephy_probe(struct platform_device *pdev)
{
struct uniphier_pciephy_priv *priv;
struct phy_provider *phy_provider;
struct device *dev = &pdev->dev;
struct regmap *regmap;
struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data))
return -EINVAL;
priv->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get(dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->rst = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
"socionext,syscon");
if (!IS_ERR(regmap) && priv->data->has_syscon)
regmap_update_bits(regmap, SG_USBPCIESEL,
SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct uniphier_pciephy_soc_data uniphier_ld20_data = {
.has_syscon = true,
};
static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = {
.has_syscon = false,
};
static const struct of_device_id uniphier_pciephy_match[] = {
{
.compatible = "socionext,uniphier-ld20-pcie-phy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-pxs3-pcie-phy",
.data = &uniphier_pxs3_data,
},
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, uniphier_pciephy_match);
static struct platform_driver uniphier_pciephy_driver = {
.probe = uniphier_pciephy_probe,
.driver = {
.name = "uniphier-pcie-phy",
.of_match_table = uniphier_pciephy_match,
},
};
module_platform_driver(uniphier_pciephy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier PHY driver for PCIe controller");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,244 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-usb2.c - PHY driver for UniPhier USB2 controller
* Copyright 2015-2018 Socionext Inc.
* Author:
* Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
*/
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#define SG_USBPHY1CTRL 0x500
#define SG_USBPHY1CTRL2 0x504
#define SG_USBPHY2CTRL 0x508
#define SG_USBPHY2CTRL2 0x50c /* LD11 */
#define SG_USBPHY12PLL 0x50c /* Pro4 */
#define SG_USBPHY3CTRL 0x510
#define SG_USBPHY3CTRL2 0x514
#define SG_USBPHY4CTRL 0x518 /* Pro4 */
#define SG_USBPHY4CTRL2 0x51c /* Pro4 */
#define SG_USBPHY34PLL 0x51c /* Pro4 */
struct uniphier_u2phy_param {
u32 offset;
u32 value;
};
struct uniphier_u2phy_soc_data {
struct uniphier_u2phy_param config0;
struct uniphier_u2phy_param config1;
};
struct uniphier_u2phy_priv {
struct regmap *regmap;
struct phy *phy;
struct regulator *vbus;
const struct uniphier_u2phy_soc_data *data;
struct uniphier_u2phy_priv *next;
};
static int uniphier_u2phy_power_on(struct phy *phy)
{
struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
int ret = 0;
if (priv->vbus)
ret = regulator_enable(priv->vbus);
return ret;
}
static int uniphier_u2phy_power_off(struct phy *phy)
{
struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
if (priv->vbus)
regulator_disable(priv->vbus);
return 0;
}
static int uniphier_u2phy_init(struct phy *phy)
{
struct uniphier_u2phy_priv *priv = phy_get_drvdata(phy);
if (!priv->data)
return 0;
regmap_write(priv->regmap, priv->data->config0.offset,
priv->data->config0.value);
regmap_write(priv->regmap, priv->data->config1.offset,
priv->data->config1.value);
return 0;
}
static struct phy *uniphier_u2phy_xlate(struct device *dev,
struct of_phandle_args *args)
{
struct uniphier_u2phy_priv *priv = dev_get_drvdata(dev);
while (priv && args->np != priv->phy->dev.of_node)
priv = priv->next;
if (!priv) {
dev_err(dev, "Failed to find appropriate phy\n");
return ERR_PTR(-EINVAL);
}
return priv->phy;
}
static const struct phy_ops uniphier_u2phy_ops = {
.init = uniphier_u2phy_init,
.power_on = uniphier_u2phy_power_on,
.power_off = uniphier_u2phy_power_off,
.owner = THIS_MODULE,
};
static int uniphier_u2phy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *parent, *child;
struct uniphier_u2phy_priv *priv = NULL, *next = NULL;
struct phy_provider *phy_provider;
struct regmap *regmap;
const struct uniphier_u2phy_soc_data *data;
int ret, data_idx, ndatas;
data = of_device_get_match_data(dev);
if (WARN_ON(!data))
return -EINVAL;
/* get number of data */
for (ndatas = 0; data[ndatas].config0.offset; ndatas++)
;
parent = of_get_parent(dev->of_node);
regmap = syscon_node_to_regmap(parent);
of_node_put(parent);
if (IS_ERR(regmap)) {
dev_err(dev, "Failed to get regmap\n");
return PTR_ERR(regmap);
}
for_each_child_of_node(dev->of_node, child) {
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto out_put_child;
}
priv->regmap = regmap;
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER) {
ret = PTR_ERR(priv->vbus);
goto out_put_child;
}
priv->vbus = NULL;
}
priv->phy = devm_phy_create(dev, child, &uniphier_u2phy_ops);
if (IS_ERR(priv->phy)) {
dev_err(dev, "Failed to create phy\n");
ret = PTR_ERR(priv->phy);
goto out_put_child;
}
ret = of_property_read_u32(child, "reg", &data_idx);
if (ret) {
dev_err(dev, "Failed to get reg property\n");
goto out_put_child;
}
if (data_idx < ndatas)
priv->data = &data[data_idx];
else
dev_warn(dev, "No phy configuration: %s\n",
child->full_name);
phy_set_drvdata(priv->phy, priv);
priv->next = next;
next = priv;
}
dev_set_drvdata(dev, priv);
phy_provider = devm_of_phy_provider_register(dev,
uniphier_u2phy_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
out_put_child:
of_node_put(child);
return ret;
}
static const struct uniphier_u2phy_soc_data uniphier_pro4_data[] = {
{
.config0 = { SG_USBPHY1CTRL, 0x05142400 },
.config1 = { SG_USBPHY12PLL, 0x00010010 },
},
{
.config0 = { SG_USBPHY2CTRL, 0x05142400 },
.config1 = { SG_USBPHY12PLL, 0x00010010 },
},
{
.config0 = { SG_USBPHY3CTRL, 0x05142400 },
.config1 = { SG_USBPHY34PLL, 0x00010010 },
},
{
.config0 = { SG_USBPHY4CTRL, 0x05142400 },
.config1 = { SG_USBPHY34PLL, 0x00010010 },
},
{ /* sentinel */ }
};
static const struct uniphier_u2phy_soc_data uniphier_ld11_data[] = {
{
.config0 = { SG_USBPHY1CTRL, 0x82280000 },
.config1 = { SG_USBPHY1CTRL2, 0x00000106 },
},
{
.config0 = { SG_USBPHY2CTRL, 0x82280000 },
.config1 = { SG_USBPHY2CTRL2, 0x00000106 },
},
{
.config0 = { SG_USBPHY3CTRL, 0x82280000 },
.config1 = { SG_USBPHY3CTRL2, 0x00000106 },
},
{ /* sentinel */ }
};
static const struct of_device_id uniphier_u2phy_match[] = {
{
.compatible = "socionext,uniphier-pro4-usb2-phy",
.data = &uniphier_pro4_data,
},
{
.compatible = "socionext,uniphier-ld11-usb2-phy",
.data = &uniphier_ld11_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u2phy_match);
static struct platform_driver uniphier_u2phy_driver = {
.probe = uniphier_u2phy_probe,
.driver = {
.name = "uniphier-usb2-phy",
.of_match_table = uniphier_u2phy_match,
},
};
module_platform_driver(uniphier_u2phy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier PHY driver for USB2 controller");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,422 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-usb3hs.c - HS-PHY driver for Socionext UniPhier USB3 controller
* Copyright 2015-2018 Socionext Inc.
* Author:
* Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
* Contributors:
* Motoya Tanigawa <tanigawa.motoya@socionext.com>
* Masami Hiramatsu <masami.hiramatsu@linaro.org>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#define HSPHY_CFG0 0x0
#define HSPHY_CFG0_HS_I_MASK GENMASK(31, 28)
#define HSPHY_CFG0_HSDISC_MASK GENMASK(27, 26)
#define HSPHY_CFG0_SWING_MASK GENMASK(17, 16)
#define HSPHY_CFG0_SEL_T_MASK GENMASK(15, 12)
#define HSPHY_CFG0_RTERM_MASK GENMASK(7, 6)
#define HSPHY_CFG0_TRIMMASK (HSPHY_CFG0_HS_I_MASK \
| HSPHY_CFG0_SEL_T_MASK \
| HSPHY_CFG0_RTERM_MASK)
#define HSPHY_CFG1 0x4
#define HSPHY_CFG1_DAT_EN BIT(29)
#define HSPHY_CFG1_ADR_EN BIT(28)
#define HSPHY_CFG1_ADR_MASK GENMASK(27, 16)
#define HSPHY_CFG1_DAT_MASK GENMASK(23, 16)
#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
#define LS_SLEW PHY_F(10, 6, 6) /* LS mode slew rate */
#define FS_LS_DRV PHY_F(10, 5, 5) /* FS/LS slew rate */
#define MAX_PHY_PARAMS 2
struct uniphier_u3hsphy_param {
struct {
int reg_no;
int msb;
int lsb;
} field;
u8 value;
};
struct uniphier_u3hsphy_trim_param {
unsigned int rterm;
unsigned int sel_t;
unsigned int hs_i;
};
#define trim_param_is_valid(p) ((p)->rterm || (p)->sel_t || (p)->hs_i)
struct uniphier_u3hsphy_priv {
struct device *dev;
void __iomem *base;
struct clk *clk, *clk_parent, *clk_ext;
struct reset_control *rst, *rst_parent;
struct regulator *vbus;
const struct uniphier_u3hsphy_soc_data *data;
};
struct uniphier_u3hsphy_soc_data {
int nparams;
const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS];
u32 config0;
u32 config1;
void (*trim_func)(struct uniphier_u3hsphy_priv *priv, u32 *pconfig,
struct uniphier_u3hsphy_trim_param *pt);
};
static void uniphier_u3hsphy_trim_ld20(struct uniphier_u3hsphy_priv *priv,
u32 *pconfig,
struct uniphier_u3hsphy_trim_param *pt)
{
*pconfig &= ~HSPHY_CFG0_RTERM_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_RTERM_MASK, pt->rterm);
*pconfig &= ~HSPHY_CFG0_SEL_T_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_SEL_T_MASK, pt->sel_t);
*pconfig &= ~HSPHY_CFG0_HS_I_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_HS_I_MASK, pt->hs_i);
}
static int uniphier_u3hsphy_get_nvparam(struct uniphier_u3hsphy_priv *priv,
const char *name, unsigned int *val)
{
struct nvmem_cell *cell;
u8 *buf;
cell = devm_nvmem_cell_get(priv->dev, name);
if (IS_ERR(cell))
return PTR_ERR(cell);
buf = nvmem_cell_read(cell, NULL);
if (IS_ERR(buf))
return PTR_ERR(buf);
*val = *buf;
kfree(buf);
return 0;
}
static int uniphier_u3hsphy_get_nvparams(struct uniphier_u3hsphy_priv *priv,
struct uniphier_u3hsphy_trim_param *pt)
{
int ret;
ret = uniphier_u3hsphy_get_nvparam(priv, "rterm", &pt->rterm);
if (ret)
return ret;
ret = uniphier_u3hsphy_get_nvparam(priv, "sel_t", &pt->sel_t);
if (ret)
return ret;
ret = uniphier_u3hsphy_get_nvparam(priv, "hs_i", &pt->hs_i);
if (ret)
return ret;
return 0;
}
static int uniphier_u3hsphy_update_config(struct uniphier_u3hsphy_priv *priv,
u32 *pconfig)
{
struct uniphier_u3hsphy_trim_param trim;
int ret, trimmed = 0;
if (priv->data->trim_func) {
ret = uniphier_u3hsphy_get_nvparams(priv, &trim);
if (ret == -EPROBE_DEFER)
return ret;
/*
* call trim_func only when trimming parameters that aren't
* all-zero can be acquired. All-zero parameters mean nothing
* has been written to nvmem.
*/
if (!ret && trim_param_is_valid(&trim)) {
priv->data->trim_func(priv, pconfig, &trim);
trimmed = 1;
} else {
dev_dbg(priv->dev, "can't get parameter from nvmem\n");
}
}
/* use default parameters without trimming values */
if (!trimmed) {
*pconfig &= ~HSPHY_CFG0_HSDISC_MASK;
*pconfig |= FIELD_PREP(HSPHY_CFG0_HSDISC_MASK, 3);
}
return 0;
}
static void uniphier_u3hsphy_set_param(struct uniphier_u3hsphy_priv *priv,
const struct uniphier_u3hsphy_param *p)
{
u32 val;
u32 field_mask = GENMASK(p->field.msb, p->field.lsb);
u8 data;
val = readl(priv->base + HSPHY_CFG1);
val &= ~HSPHY_CFG1_ADR_MASK;
val |= FIELD_PREP(HSPHY_CFG1_ADR_MASK, p->field.reg_no)
| HSPHY_CFG1_ADR_EN;
writel(val, priv->base + HSPHY_CFG1);
val = readl(priv->base + HSPHY_CFG1);
val &= ~HSPHY_CFG1_ADR_EN;
writel(val, priv->base + HSPHY_CFG1);
val = readl(priv->base + HSPHY_CFG1);
val &= ~FIELD_PREP(HSPHY_CFG1_DAT_MASK, field_mask);
data = field_mask & (p->value << p->field.lsb);
val |= FIELD_PREP(HSPHY_CFG1_DAT_MASK, data) | HSPHY_CFG1_DAT_EN;
writel(val, priv->base + HSPHY_CFG1);
val = readl(priv->base + HSPHY_CFG1);
val &= ~HSPHY_CFG1_DAT_EN;
writel(val, priv->base + HSPHY_CFG1);
}
static int uniphier_u3hsphy_power_on(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk_ext);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
goto out_clk_ext_disable;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_disable;
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
goto out_rst_assert;
}
return 0;
out_rst_assert:
reset_control_assert(priv->rst);
out_clk_disable:
clk_disable_unprepare(priv->clk);
out_clk_ext_disable:
clk_disable_unprepare(priv->clk_ext);
return ret;
}
static int uniphier_u3hsphy_power_off(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
if (priv->vbus)
regulator_disable(priv->vbus);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->clk_ext);
return 0;
}
static int uniphier_u3hsphy_init(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
u32 config0, config1;
int i, ret;
ret = clk_prepare_enable(priv->clk_parent);
if (ret)
return ret;
ret = reset_control_deassert(priv->rst_parent);
if (ret)
goto out_clk_disable;
if (!priv->data->config0 && !priv->data->config1)
return 0;
config0 = priv->data->config0;
config1 = priv->data->config1;
ret = uniphier_u3hsphy_update_config(priv, &config0);
if (ret)
goto out_rst_assert;
writel(config0, priv->base + HSPHY_CFG0);
writel(config1, priv->base + HSPHY_CFG1);
for (i = 0; i < priv->data->nparams; i++)
uniphier_u3hsphy_set_param(priv, &priv->data->param[i]);
return 0;
out_rst_assert:
reset_control_assert(priv->rst_parent);
out_clk_disable:
clk_disable_unprepare(priv->clk_parent);
return ret;
}
static int uniphier_u3hsphy_exit(struct phy *phy)
{
struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
reset_control_assert(priv->rst_parent);
clk_disable_unprepare(priv->clk_parent);
return 0;
}
static const struct phy_ops uniphier_u3hsphy_ops = {
.init = uniphier_u3hsphy_init,
.exit = uniphier_u3hsphy_exit,
.power_on = uniphier_u3hsphy_power_on,
.power_off = uniphier_u3hsphy_power_off,
.owner = THIS_MODULE,
};
static int uniphier_u3hsphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_u3hsphy_priv *priv;
struct phy_provider *phy_provider;
struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data ||
priv->data->nparams > MAX_PHY_PARAMS))
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get(dev, "phy");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->clk_parent = devm_clk_get(dev, "link");
if (IS_ERR(priv->clk_parent))
return PTR_ERR(priv->clk_parent);
priv->clk_ext = devm_clk_get(dev, "phy-ext");
if (IS_ERR(priv->clk_ext)) {
if (PTR_ERR(priv->clk_ext) == -ENOENT)
priv->clk_ext = NULL;
else
return PTR_ERR(priv->clk_ext);
}
priv->rst = devm_reset_control_get_shared(dev, "phy");
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
priv->rst_parent = devm_reset_control_get_shared(dev, "link");
if (IS_ERR(priv->rst_parent))
return PTR_ERR(priv->rst_parent);
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
return PTR_ERR(priv->vbus);
priv->vbus = NULL;
}
phy = devm_phy_create(dev, dev->of_node, &uniphier_u3hsphy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {
.nparams = 0,
};
static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
.nparams = 2,
.param = {
{ LS_SLEW, 1 },
{ FS_LS_DRV, 1 },
},
.trim_func = uniphier_u3hsphy_trim_ld20,
.config0 = 0x92316680,
.config1 = 0x00000106,
};
static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = {
.nparams = 0,
.trim_func = uniphier_u3hsphy_trim_ld20,
.config0 = 0x92316680,
.config1 = 0x00000106,
};
static const struct of_device_id uniphier_u3hsphy_match[] = {
{
.compatible = "socionext,uniphier-pxs2-usb3-hsphy",
.data = &uniphier_pxs2_data,
},
{
.compatible = "socionext,uniphier-ld20-usb3-hsphy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-pxs3-usb3-hsphy",
.data = &uniphier_pxs3_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match);
static struct platform_driver uniphier_u3hsphy_driver = {
.probe = uniphier_u3hsphy_probe,
.driver = {
.name = "uniphier-usb3-hsphy",
.of_match_table = uniphier_u3hsphy_match,
},
};
module_platform_driver(uniphier_u3hsphy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier HS-PHY driver for USB3 controller");
MODULE_LICENSE("GPL v2");

View File

@@ -0,0 +1,349 @@
// SPDX-License-Identifier: GPL-2.0
/*
* phy-uniphier-usb3ss.c - SS-PHY driver for Socionext UniPhier USB3 controller
* Copyright 2015-2018 Socionext Inc.
* Author:
* Kunihiko Hayashi <hayashi.kunihiko@socionext.com>
* Contributors:
* Motoya Tanigawa <tanigawa.motoya@socionext.com>
* Masami Hiramatsu <masami.hiramatsu@linaro.org>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#define SSPHY_TESTI 0x0
#define SSPHY_TESTO 0x4
#define TESTI_DAT_MASK GENMASK(13, 6)
#define TESTI_ADR_MASK GENMASK(5, 1)
#define TESTI_WR_EN BIT(0)
#define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
#define CDR_CPD_TRIM PHY_F(7, 3, 0) /* RxPLL charge pump current */
#define CDR_CPF_TRIM PHY_F(8, 3, 0) /* RxPLL charge pump current 2 */
#define TX_PLL_TRIM PHY_F(9, 3, 0) /* TxPLL charge pump current */
#define BGAP_TRIM PHY_F(11, 3, 0) /* Bandgap voltage */
#define CDR_TRIM PHY_F(13, 6, 5) /* Clock Data Recovery setting */
#define VCO_CTRL PHY_F(26, 7, 4) /* VCO control */
#define VCOPLL_CTRL PHY_F(27, 2, 0) /* TxPLL VCO tuning */
#define VCOPLL_CM PHY_F(28, 1, 0) /* TxPLL voltage */
#define MAX_PHY_PARAMS 7
struct uniphier_u3ssphy_param {
struct {
int reg_no;
int msb;
int lsb;
} field;
u8 value;
};
struct uniphier_u3ssphy_priv {
struct device *dev;
void __iomem *base;
struct clk *clk, *clk_ext, *clk_parent, *clk_parent_gio;
struct reset_control *rst, *rst_parent, *rst_parent_gio;
struct regulator *vbus;
const struct uniphier_u3ssphy_soc_data *data;
};
struct uniphier_u3ssphy_soc_data {
bool is_legacy;
int nparams;
const struct uniphier_u3ssphy_param param[MAX_PHY_PARAMS];
};
static void uniphier_u3ssphy_testio_write(struct uniphier_u3ssphy_priv *priv,
u32 data)
{
/* need to read TESTO twice after accessing TESTI */
writel(data, priv->base + SSPHY_TESTI);
readl(priv->base + SSPHY_TESTO);
readl(priv->base + SSPHY_TESTO);
}
static void uniphier_u3ssphy_set_param(struct uniphier_u3ssphy_priv *priv,
const struct uniphier_u3ssphy_param *p)
{
u32 val;
u8 field_mask = GENMASK(p->field.msb, p->field.lsb);
u8 data;
/* read previous data */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
val = readl(priv->base + SSPHY_TESTO);
/* update value */
val &= ~FIELD_PREP(TESTI_DAT_MASK, field_mask);
data = field_mask & (p->value << p->field.lsb);
val = FIELD_PREP(TESTI_DAT_MASK, data);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
uniphier_u3ssphy_testio_write(priv, val | TESTI_WR_EN);
uniphier_u3ssphy_testio_write(priv, val);
/* read current data as dummy */
val = FIELD_PREP(TESTI_DAT_MASK, 1);
val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no);
uniphier_u3ssphy_testio_write(priv, val);
readl(priv->base + SSPHY_TESTO);
}
static int uniphier_u3ssphy_power_on(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
int ret;
ret = clk_prepare_enable(priv->clk_ext);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk);
if (ret)
goto out_clk_ext_disable;
ret = reset_control_deassert(priv->rst);
if (ret)
goto out_clk_disable;
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
goto out_rst_assert;
}
return 0;
out_rst_assert:
reset_control_assert(priv->rst);
out_clk_disable:
clk_disable_unprepare(priv->clk);
out_clk_ext_disable:
clk_disable_unprepare(priv->clk_ext);
return ret;
}
static int uniphier_u3ssphy_power_off(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
if (priv->vbus)
regulator_disable(priv->vbus);
reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->clk_ext);
return 0;
}
static int uniphier_u3ssphy_init(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
int i, ret;
ret = clk_prepare_enable(priv->clk_parent);
if (ret)
return ret;
ret = clk_prepare_enable(priv->clk_parent_gio);
if (ret)
goto out_clk_disable;
ret = reset_control_deassert(priv->rst_parent);
if (ret)
goto out_clk_gio_disable;
ret = reset_control_deassert(priv->rst_parent_gio);
if (ret)
goto out_rst_assert;
if (priv->data->is_legacy)
return 0;
for (i = 0; i < priv->data->nparams; i++)
uniphier_u3ssphy_set_param(priv, &priv->data->param[i]);
return 0;
out_rst_assert:
reset_control_assert(priv->rst_parent);
out_clk_gio_disable:
clk_disable_unprepare(priv->clk_parent_gio);
out_clk_disable:
clk_disable_unprepare(priv->clk_parent);
return ret;
}
static int uniphier_u3ssphy_exit(struct phy *phy)
{
struct uniphier_u3ssphy_priv *priv = phy_get_drvdata(phy);
reset_control_assert(priv->rst_parent_gio);
reset_control_assert(priv->rst_parent);
clk_disable_unprepare(priv->clk_parent_gio);
clk_disable_unprepare(priv->clk_parent);
return 0;
}
static const struct phy_ops uniphier_u3ssphy_ops = {
.init = uniphier_u3ssphy_init,
.exit = uniphier_u3ssphy_exit,
.power_on = uniphier_u3ssphy_power_on,
.power_off = uniphier_u3ssphy_power_off,
.owner = THIS_MODULE,
};
static int uniphier_u3ssphy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct uniphier_u3ssphy_priv *priv;
struct phy_provider *phy_provider;
struct resource *res;
struct phy *phy;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->dev = dev;
priv->data = of_device_get_match_data(dev);
if (WARN_ON(!priv->data ||
priv->data->nparams > MAX_PHY_PARAMS))
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
if (!priv->data->is_legacy) {
priv->clk = devm_clk_get(dev, "phy");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
priv->clk_ext = devm_clk_get(dev, "phy-ext");
if (IS_ERR(priv->clk_ext)) {
if (PTR_ERR(priv->clk_ext) == -ENOENT)
priv->clk_ext = NULL;
else
return PTR_ERR(priv->clk_ext);
}
priv->rst = devm_reset_control_get_shared(dev, "phy");
if (IS_ERR(priv->rst))
return PTR_ERR(priv->rst);
} else {
priv->clk_parent_gio = devm_clk_get(dev, "gio");
if (IS_ERR(priv->clk_parent_gio))
return PTR_ERR(priv->clk_parent_gio);
priv->rst_parent_gio =
devm_reset_control_get_shared(dev, "gio");
if (IS_ERR(priv->rst_parent_gio))
return PTR_ERR(priv->rst_parent_gio);
}
priv->clk_parent = devm_clk_get(dev, "link");
if (IS_ERR(priv->clk_parent))
return PTR_ERR(priv->clk_parent);
priv->rst_parent = devm_reset_control_get_shared(dev, "link");
if (IS_ERR(priv->rst_parent))
return PTR_ERR(priv->rst_parent);
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
return PTR_ERR(priv->vbus);
priv->vbus = NULL;
}
phy = devm_phy_create(dev, dev->of_node, &uniphier_u3ssphy_ops);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
return PTR_ERR_OR_ZERO(phy_provider);
}
static const struct uniphier_u3ssphy_soc_data uniphier_pro4_data = {
.is_legacy = true,
};
static const struct uniphier_u3ssphy_soc_data uniphier_pxs2_data = {
.is_legacy = false,
.nparams = 7,
.param = {
{ CDR_CPD_TRIM, 10 },
{ CDR_CPF_TRIM, 3 },
{ TX_PLL_TRIM, 5 },
{ BGAP_TRIM, 9 },
{ CDR_TRIM, 2 },
{ VCOPLL_CTRL, 7 },
{ VCOPLL_CM, 1 },
},
};
static const struct uniphier_u3ssphy_soc_data uniphier_ld20_data = {
.is_legacy = false,
.nparams = 3,
.param = {
{ CDR_CPD_TRIM, 6 },
{ CDR_TRIM, 2 },
{ VCO_CTRL, 5 },
},
};
static const struct of_device_id uniphier_u3ssphy_match[] = {
{
.compatible = "socionext,uniphier-pro4-usb3-ssphy",
.data = &uniphier_pro4_data,
},
{
.compatible = "socionext,uniphier-pxs2-usb3-ssphy",
.data = &uniphier_pxs2_data,
},
{
.compatible = "socionext,uniphier-ld20-usb3-ssphy",
.data = &uniphier_ld20_data,
},
{
.compatible = "socionext,uniphier-pxs3-usb3-ssphy",
.data = &uniphier_ld20_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, uniphier_u3ssphy_match);
static struct platform_driver uniphier_u3ssphy_driver = {
.probe = uniphier_u3ssphy_probe,
.driver = {
.name = "uniphier-usb3-ssphy",
.of_match_table = uniphier_u3ssphy_match,
},
};
module_platform_driver(uniphier_u3ssphy_driver);
MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
MODULE_DESCRIPTION("UniPhier SS-PHY driver for USB3 controller");
MODULE_LICENSE("GPL v2");

View File

@@ -115,8 +115,8 @@ int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
err = match_string(lane->soc->funcs, lane->soc->num_funcs, function);
if (err < 0) {
dev_err(dev, "invalid function \"%s\" for lane \"%s\"\n",
function, np->name);
dev_err(dev, "invalid function \"%s\" for lane \"%pOFn\"\n",
function, np);
return err;
}

View File

@@ -144,6 +144,7 @@
#define PMBR1 0x0D
#define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
static irqreturn_t twl4030_usb_irq(int irq, void *_twl);
/*
* If VBUS is valid or ID is ground, then we know a
* cable is present and we need to be runtime-enabled
@@ -395,6 +396,33 @@ static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
}
static int __maybe_unused twl4030_usb_suspend(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
/*
* we need enabled runtime on resume,
* so turn irq off here, so we do not get it early
* note: wakeup on usb plug works independently of this
*/
dev_dbg(twl->dev, "%s\n", __func__);
disable_irq(twl->irq);
return 0;
}
static int __maybe_unused twl4030_usb_resume(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
dev_dbg(twl->dev, "%s\n", __func__);
enable_irq(twl->irq);
/* check whether cable status changed */
twl4030_usb_irq(0, twl);
return 0;
}
static int __maybe_unused twl4030_usb_runtime_suspend(struct device *dev)
{
struct twl4030_usb *twl = dev_get_drvdata(dev);
@@ -655,6 +683,7 @@ static const struct phy_ops ops = {
static const struct dev_pm_ops twl4030_usb_pm_ops = {
SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend,
twl4030_usb_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(twl4030_usb_suspend, twl4030_usb_resume)
};
static int twl4030_usb_probe(struct platform_device *pdev)