162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Samsung Exynos5 SoC series USB DRD PHY driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2014 Samsung Electronics Co., Ltd. 862306a36Sopenharmony_ci * Author: Vivek Gautam <gautam.vivek@samsung.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/iopoll.h> 1862306a36Sopenharmony_ci#include <linux/phy/phy.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/mutex.h> 2162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2262306a36Sopenharmony_ci#include <linux/regmap.h> 2362306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2462306a36Sopenharmony_ci#include <linux/soc/samsung/exynos-regs-pmu.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Exynos USB PHY registers */ 2762306a36Sopenharmony_ci#define EXYNOS5_FSEL_9MHZ6 0x0 2862306a36Sopenharmony_ci#define EXYNOS5_FSEL_10MHZ 0x1 2962306a36Sopenharmony_ci#define EXYNOS5_FSEL_12MHZ 0x2 3062306a36Sopenharmony_ci#define EXYNOS5_FSEL_19MHZ2 0x3 3162306a36Sopenharmony_ci#define EXYNOS5_FSEL_20MHZ 0x4 3262306a36Sopenharmony_ci#define EXYNOS5_FSEL_24MHZ 0x5 3362306a36Sopenharmony_ci#define EXYNOS5_FSEL_26MHZ 0x82 3462306a36Sopenharmony_ci#define EXYNOS5_FSEL_50MHZ 0x7 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* Exynos5: USB 3.0 DRD PHY registers */ 3762306a36Sopenharmony_ci#define EXYNOS5_DRD_LINKSYSTEM 0x04 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define LINKSYSTEM_FLADJ_MASK (0x3f << 1) 4062306a36Sopenharmony_ci#define LINKSYSTEM_FLADJ(_x) ((_x) << 1) 4162306a36Sopenharmony_ci#define LINKSYSTEM_XHCI_VERSION_CONTROL BIT(27) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYUTMI 0x08 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define PHYUTMI_OTGDISABLE BIT(6) 4662306a36Sopenharmony_ci#define PHYUTMI_FORCESUSPEND BIT(1) 4762306a36Sopenharmony_ci#define PHYUTMI_FORCESLEEP BIT(0) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYPIPE 0x0c 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYCLKRST 0x10 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define PHYCLKRST_EN_UTMISUSPEND BIT(31) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define PHYCLKRST_SSC_REFCLKSEL_MASK (0xff << 23) 5662306a36Sopenharmony_ci#define PHYCLKRST_SSC_REFCLKSEL(_x) ((_x) << 23) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define PHYCLKRST_SSC_RANGE_MASK (0x03 << 21) 5962306a36Sopenharmony_ci#define PHYCLKRST_SSC_RANGE(_x) ((_x) << 21) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define PHYCLKRST_SSC_EN BIT(20) 6262306a36Sopenharmony_ci#define PHYCLKRST_REF_SSP_EN BIT(19) 6362306a36Sopenharmony_ci#define PHYCLKRST_REF_CLKDIV2 BIT(18) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define PHYCLKRST_MPLL_MULTIPLIER_MASK (0x7f << 11) 6662306a36Sopenharmony_ci#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF (0x19 << 11) 6762306a36Sopenharmony_ci#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF (0x32 << 11) 6862306a36Sopenharmony_ci#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF (0x68 << 11) 6962306a36Sopenharmony_ci#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF (0x7d << 11) 7062306a36Sopenharmony_ci#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define PHYCLKRST_FSEL_UTMI_MASK (0x7 << 5) 7362306a36Sopenharmony_ci#define PHYCLKRST_FSEL_PIPE_MASK (0x7 << 8) 7462306a36Sopenharmony_ci#define PHYCLKRST_FSEL(_x) ((_x) << 5) 7562306a36Sopenharmony_ci#define PHYCLKRST_FSEL_PAD_100MHZ (0x27 << 5) 7662306a36Sopenharmony_ci#define PHYCLKRST_FSEL_PAD_24MHZ (0x2a << 5) 7762306a36Sopenharmony_ci#define PHYCLKRST_FSEL_PAD_20MHZ (0x31 << 5) 7862306a36Sopenharmony_ci#define PHYCLKRST_FSEL_PAD_19_2MHZ (0x38 << 5) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define PHYCLKRST_RETENABLEN BIT(4) 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define PHYCLKRST_REFCLKSEL_MASK (0x03 << 2) 8362306a36Sopenharmony_ci#define PHYCLKRST_REFCLKSEL_PAD_REFCLK (0x2 << 2) 8462306a36Sopenharmony_ci#define PHYCLKRST_REFCLKSEL_EXT_REFCLK (0x3 << 2) 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define PHYCLKRST_PORTRESET BIT(1) 8762306a36Sopenharmony_ci#define PHYCLKRST_COMMONONN BIT(0) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYREG0 0x14 9062306a36Sopenharmony_ci#define PHYREG0_SSC_REF_CLK_SEL BIT(21) 9162306a36Sopenharmony_ci#define PHYREG0_SSC_RANGE BIT(20) 9262306a36Sopenharmony_ci#define PHYREG0_CR_WRITE BIT(19) 9362306a36Sopenharmony_ci#define PHYREG0_CR_READ BIT(18) 9462306a36Sopenharmony_ci#define PHYREG0_CR_DATA_IN(_x) ((_x) << 2) 9562306a36Sopenharmony_ci#define PHYREG0_CR_CAP_DATA BIT(1) 9662306a36Sopenharmony_ci#define PHYREG0_CR_CAP_ADDR BIT(0) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYREG1 0x18 9962306a36Sopenharmony_ci#define PHYREG1_CR_DATA_OUT(_x) ((_x) << 1) 10062306a36Sopenharmony_ci#define PHYREG1_CR_ACK BIT(0) 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYPARAM0 0x1c 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define PHYPARAM0_REF_USE_PAD BIT(31) 10562306a36Sopenharmony_ci#define PHYPARAM0_REF_LOSLEVEL_MASK (0x1f << 26) 10662306a36Sopenharmony_ci#define PHYPARAM0_REF_LOSLEVEL (0x9 << 26) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYPARAM1 0x20 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define PHYPARAM1_PCS_TXDEEMPH_MASK (0x1f << 0) 11162306a36Sopenharmony_ci#define PHYPARAM1_PCS_TXDEEMPH (0x1c) 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYTERM 0x24 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYTEST 0x28 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define PHYTEST_POWERDOWN_SSP BIT(3) 11862306a36Sopenharmony_ci#define PHYTEST_POWERDOWN_HSP BIT(2) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYADP 0x2c 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYUTMICLKSEL 0x30 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#define PHYUTMICLKSEL_UTMI_CLKSEL BIT(2) 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYRESUME 0x34 12762306a36Sopenharmony_ci#define EXYNOS5_DRD_LINKPORT 0x44 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */ 13062306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN (0x15) 13162306a36Sopenharmony_ci#define LOSLEVEL_OVRD_IN_LOS_BIAS_5420 (0x5 << 13) 13262306a36Sopenharmony_ci#define LOSLEVEL_OVRD_IN_LOS_BIAS_DEFAULT (0x0 << 13) 13362306a36Sopenharmony_ci#define LOSLEVEL_OVRD_IN_EN (0x1 << 10) 13462306a36Sopenharmony_ci#define LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT (0x9 << 0) 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN (0x12) 13762306a36Sopenharmony_ci#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420 (0x5 << 13) 13862306a36Sopenharmony_ci#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_DEFAULT (0x4 << 13) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG (0x1010) 14162306a36Sopenharmony_ci#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M (0x4 << 4) 14262306a36Sopenharmony_ci#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M (0x8 << 4) 14362306a36Sopenharmony_ci#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_25M_26M (0x8 << 4) 14462306a36Sopenharmony_ci#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M (0x20 << 4) 14562306a36Sopenharmony_ci#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5 (0x20 << 4) 14662306a36Sopenharmony_ci#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M (0x40 << 4) 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/* Exynos850: USB DRD PHY registers */ 14962306a36Sopenharmony_ci#define EXYNOS850_DRD_LINKCTRL 0x04 15062306a36Sopenharmony_ci#define LINKCTRL_BUS_FILTER_BYPASS(_x) ((_x) << 4) 15162306a36Sopenharmony_ci#define LINKCTRL_FORCE_QACT BIT(8) 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci#define EXYNOS850_DRD_CLKRST 0x20 15462306a36Sopenharmony_ci#define CLKRST_LINK_SW_RST BIT(0) 15562306a36Sopenharmony_ci#define CLKRST_PORT_RST BIT(1) 15662306a36Sopenharmony_ci#define CLKRST_PHY_SW_RST BIT(3) 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define EXYNOS850_DRD_UTMI 0x50 15962306a36Sopenharmony_ci#define UTMI_FORCE_SLEEP BIT(0) 16062306a36Sopenharmony_ci#define UTMI_FORCE_SUSPEND BIT(1) 16162306a36Sopenharmony_ci#define UTMI_DM_PULLDOWN BIT(2) 16262306a36Sopenharmony_ci#define UTMI_DP_PULLDOWN BIT(3) 16362306a36Sopenharmony_ci#define UTMI_FORCE_BVALID BIT(4) 16462306a36Sopenharmony_ci#define UTMI_FORCE_VBUSVALID BIT(5) 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#define EXYNOS850_DRD_HSP 0x54 16762306a36Sopenharmony_ci#define HSP_COMMONONN BIT(8) 16862306a36Sopenharmony_ci#define HSP_EN_UTMISUSPEND BIT(9) 16962306a36Sopenharmony_ci#define HSP_VBUSVLDEXT BIT(12) 17062306a36Sopenharmony_ci#define HSP_VBUSVLDEXTSEL BIT(13) 17162306a36Sopenharmony_ci#define HSP_FSV_OUT_EN BIT(24) 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#define EXYNOS850_DRD_HSP_TEST 0x5c 17462306a36Sopenharmony_ci#define HSP_TEST_SIDDQ BIT(24) 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci#define KHZ 1000 17762306a36Sopenharmony_ci#define MHZ (KHZ * KHZ) 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cienum exynos5_usbdrd_phy_id { 18062306a36Sopenharmony_ci EXYNOS5_DRDPHY_UTMI, 18162306a36Sopenharmony_ci EXYNOS5_DRDPHY_PIPE3, 18262306a36Sopenharmony_ci EXYNOS5_DRDPHYS_NUM, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct phy_usb_instance; 18662306a36Sopenharmony_cistruct exynos5_usbdrd_phy; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistruct exynos5_usbdrd_phy_config { 18962306a36Sopenharmony_ci u32 id; 19062306a36Sopenharmony_ci void (*phy_isol)(struct phy_usb_instance *inst, u32 on); 19162306a36Sopenharmony_ci void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd); 19262306a36Sopenharmony_ci unsigned int (*set_refclk)(struct phy_usb_instance *inst); 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistruct exynos5_usbdrd_phy_drvdata { 19662306a36Sopenharmony_ci const struct exynos5_usbdrd_phy_config *phy_cfg; 19762306a36Sopenharmony_ci const struct phy_ops *phy_ops; 19862306a36Sopenharmony_ci u32 pmu_offset_usbdrd0_phy; 19962306a36Sopenharmony_ci u32 pmu_offset_usbdrd1_phy; 20062306a36Sopenharmony_ci bool has_common_clk_gate; 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci/** 20462306a36Sopenharmony_ci * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY 20562306a36Sopenharmony_ci * @dev: pointer to device instance of this platform device 20662306a36Sopenharmony_ci * @reg_phy: usb phy controller register memory base 20762306a36Sopenharmony_ci * @clk: phy clock for register access 20862306a36Sopenharmony_ci * @pipeclk: clock for pipe3 phy 20962306a36Sopenharmony_ci * @utmiclk: clock for utmi+ phy 21062306a36Sopenharmony_ci * @itpclk: clock for ITP generation 21162306a36Sopenharmony_ci * @drv_data: pointer to SoC level driver data structure 21262306a36Sopenharmony_ci * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY 21362306a36Sopenharmony_ci * instances each with its 'phy' and 'phy_cfg'. 21462306a36Sopenharmony_ci * @extrefclk: frequency select settings when using 'separate 21562306a36Sopenharmony_ci * reference clocks' for SS and HS operations 21662306a36Sopenharmony_ci * @ref_clk: reference clock to PHY block from which PHY's 21762306a36Sopenharmony_ci * operational clocks are derived 21862306a36Sopenharmony_ci * @vbus: VBUS regulator for phy 21962306a36Sopenharmony_ci * @vbus_boost: Boost regulator for VBUS present on few Exynos boards 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_cistruct exynos5_usbdrd_phy { 22262306a36Sopenharmony_ci struct device *dev; 22362306a36Sopenharmony_ci void __iomem *reg_phy; 22462306a36Sopenharmony_ci struct clk *clk; 22562306a36Sopenharmony_ci struct clk *pipeclk; 22662306a36Sopenharmony_ci struct clk *utmiclk; 22762306a36Sopenharmony_ci struct clk *itpclk; 22862306a36Sopenharmony_ci const struct exynos5_usbdrd_phy_drvdata *drv_data; 22962306a36Sopenharmony_ci struct phy_usb_instance { 23062306a36Sopenharmony_ci struct phy *phy; 23162306a36Sopenharmony_ci u32 index; 23262306a36Sopenharmony_ci struct regmap *reg_pmu; 23362306a36Sopenharmony_ci u32 pmu_offset; 23462306a36Sopenharmony_ci const struct exynos5_usbdrd_phy_config *phy_cfg; 23562306a36Sopenharmony_ci } phys[EXYNOS5_DRDPHYS_NUM]; 23662306a36Sopenharmony_ci u32 extrefclk; 23762306a36Sopenharmony_ci struct clk *ref_clk; 23862306a36Sopenharmony_ci struct regulator *vbus; 23962306a36Sopenharmony_ci struct regulator *vbus_boost; 24062306a36Sopenharmony_ci}; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic inline 24362306a36Sopenharmony_cistruct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci return container_of((inst), struct exynos5_usbdrd_phy, 24662306a36Sopenharmony_ci phys[(inst)->index]); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* 25062306a36Sopenharmony_ci * exynos5_rate_to_clk() converts the supplied clock rate to the value that 25162306a36Sopenharmony_ci * can be written to the phy register. 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_cistatic unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci /* EXYNOS5_FSEL_MASK */ 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci switch (rate) { 25862306a36Sopenharmony_ci case 9600 * KHZ: 25962306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_9MHZ6; 26062306a36Sopenharmony_ci break; 26162306a36Sopenharmony_ci case 10 * MHZ: 26262306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_10MHZ; 26362306a36Sopenharmony_ci break; 26462306a36Sopenharmony_ci case 12 * MHZ: 26562306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_12MHZ; 26662306a36Sopenharmony_ci break; 26762306a36Sopenharmony_ci case 19200 * KHZ: 26862306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_19MHZ2; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci case 20 * MHZ: 27162306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_20MHZ; 27262306a36Sopenharmony_ci break; 27362306a36Sopenharmony_ci case 24 * MHZ: 27462306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_24MHZ; 27562306a36Sopenharmony_ci break; 27662306a36Sopenharmony_ci case 26 * MHZ: 27762306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_26MHZ; 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case 50 * MHZ: 28062306a36Sopenharmony_ci *reg = EXYNOS5_FSEL_50MHZ; 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci default: 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return 0; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst, 29062306a36Sopenharmony_ci unsigned int on) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci unsigned int val; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (!inst->reg_pmu) 29562306a36Sopenharmony_ci return; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci val = on ? 0 : EXYNOS4_PHY_ENABLE; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci regmap_update_bits(inst->reg_pmu, inst->pmu_offset, 30062306a36Sopenharmony_ci EXYNOS4_PHY_ENABLE, val); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* 30462306a36Sopenharmony_ci * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock 30562306a36Sopenharmony_ci * from clock core. Further sets multiplier values and spread spectrum 30662306a36Sopenharmony_ci * clock settings for SuperSpeed operations. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_cistatic unsigned int 30962306a36Sopenharmony_ciexynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci u32 reg; 31262306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* restore any previous reference clock settings */ 31562306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Use EXTREFCLK as ref clock */ 31862306a36Sopenharmony_ci reg &= ~PHYCLKRST_REFCLKSEL_MASK; 31962306a36Sopenharmony_ci reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* FSEL settings corresponding to reference clock */ 32262306a36Sopenharmony_ci reg &= ~PHYCLKRST_FSEL_PIPE_MASK | 32362306a36Sopenharmony_ci PHYCLKRST_MPLL_MULTIPLIER_MASK | 32462306a36Sopenharmony_ci PHYCLKRST_SSC_REFCLKSEL_MASK; 32562306a36Sopenharmony_ci switch (phy_drd->extrefclk) { 32662306a36Sopenharmony_ci case EXYNOS5_FSEL_50MHZ: 32762306a36Sopenharmony_ci reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF | 32862306a36Sopenharmony_ci PHYCLKRST_SSC_REFCLKSEL(0x00)); 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci case EXYNOS5_FSEL_24MHZ: 33162306a36Sopenharmony_ci reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF | 33262306a36Sopenharmony_ci PHYCLKRST_SSC_REFCLKSEL(0x88)); 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci case EXYNOS5_FSEL_20MHZ: 33562306a36Sopenharmony_ci reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF | 33662306a36Sopenharmony_ci PHYCLKRST_SSC_REFCLKSEL(0x00)); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case EXYNOS5_FSEL_19MHZ2: 33962306a36Sopenharmony_ci reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF | 34062306a36Sopenharmony_ci PHYCLKRST_SSC_REFCLKSEL(0x88)); 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci default: 34362306a36Sopenharmony_ci dev_dbg(phy_drd->dev, "unsupported ref clk\n"); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return reg; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock 35262306a36Sopenharmony_ci * from clock core. Further sets the FSEL values for HighSpeed operations. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic unsigned int 35562306a36Sopenharmony_ciexynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci u32 reg; 35862306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* restore any previous reference clock settings */ 36162306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci reg &= ~PHYCLKRST_REFCLKSEL_MASK; 36462306a36Sopenharmony_ci reg |= PHYCLKRST_REFCLKSEL_EXT_REFCLK; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci reg &= ~PHYCLKRST_FSEL_UTMI_MASK | 36762306a36Sopenharmony_ci PHYCLKRST_MPLL_MULTIPLIER_MASK | 36862306a36Sopenharmony_ci PHYCLKRST_SSC_REFCLKSEL_MASK; 36962306a36Sopenharmony_ci reg |= PHYCLKRST_FSEL(phy_drd->extrefclk); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return reg; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci u32 reg; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); 37962306a36Sopenharmony_ci /* Set Tx De-Emphasis level */ 38062306a36Sopenharmony_ci reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; 38162306a36Sopenharmony_ci reg |= PHYPARAM1_PCS_TXDEEMPH; 38262306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); 38562306a36Sopenharmony_ci reg &= ~PHYTEST_POWERDOWN_SSP; 38662306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci u32 reg; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); 39462306a36Sopenharmony_ci /* Set Loss-of-Signal Detector sensitivity */ 39562306a36Sopenharmony_ci reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK; 39662306a36Sopenharmony_ci reg |= PHYPARAM0_REF_LOSLEVEL; 39762306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); 40062306a36Sopenharmony_ci /* Set Tx De-Emphasis level */ 40162306a36Sopenharmony_ci reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK; 40262306a36Sopenharmony_ci reg |= PHYPARAM1_PCS_TXDEEMPH; 40362306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* UTMI Power Control */ 40662306a36Sopenharmony_ci writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); 40962306a36Sopenharmony_ci reg &= ~PHYTEST_POWERDOWN_HSP; 41062306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic int exynos5_usbdrd_phy_init(struct phy *phy) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci int ret; 41662306a36Sopenharmony_ci u32 reg; 41762306a36Sopenharmony_ci struct phy_usb_instance *inst = phy_get_drvdata(phy); 41862306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ret = clk_prepare_enable(phy_drd->clk); 42162306a36Sopenharmony_ci if (ret) 42262306a36Sopenharmony_ci return ret; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* Reset USB 3.0 PHY */ 42562306a36Sopenharmony_ci writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); 42662306a36Sopenharmony_ci writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* 42962306a36Sopenharmony_ci * Setting the Frame length Adj value[6:1] to default 0x20 43062306a36Sopenharmony_ci * See xHCI 1.0 spec, 5.2.4 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci reg = LINKSYSTEM_XHCI_VERSION_CONTROL | 43362306a36Sopenharmony_ci LINKSYSTEM_FLADJ(0x20); 43462306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); 43762306a36Sopenharmony_ci /* Select PHY CLK source */ 43862306a36Sopenharmony_ci reg &= ~PHYPARAM0_REF_USE_PAD; 43962306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci /* This bit must be set for both HS and SS operations */ 44262306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); 44362306a36Sopenharmony_ci reg |= PHYUTMICLKSEL_UTMI_CLKSEL; 44462306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* UTMI or PIPE3 specific init */ 44762306a36Sopenharmony_ci inst->phy_cfg->phy_init(phy_drd); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* reference clock settings */ 45062306a36Sopenharmony_ci reg = inst->phy_cfg->set_refclk(inst); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Digital power supply in normal operating mode */ 45362306a36Sopenharmony_ci reg |= PHYCLKRST_RETENABLEN | 45462306a36Sopenharmony_ci /* Enable ref clock for SS function */ 45562306a36Sopenharmony_ci PHYCLKRST_REF_SSP_EN | 45662306a36Sopenharmony_ci /* Enable spread spectrum */ 45762306a36Sopenharmony_ci PHYCLKRST_SSC_EN | 45862306a36Sopenharmony_ci /* Power down HS Bias and PLL blocks in suspend mode */ 45962306a36Sopenharmony_ci PHYCLKRST_COMMONONN | 46062306a36Sopenharmony_ci /* Reset the port */ 46162306a36Sopenharmony_ci PHYCLKRST_PORTRESET; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci udelay(10); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci reg &= ~PHYCLKRST_PORTRESET; 46862306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->clk); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int exynos5_usbdrd_phy_exit(struct phy *phy) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci int ret; 47862306a36Sopenharmony_ci u32 reg; 47962306a36Sopenharmony_ci struct phy_usb_instance *inst = phy_get_drvdata(phy); 48062306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ret = clk_prepare_enable(phy_drd->clk); 48362306a36Sopenharmony_ci if (ret) 48462306a36Sopenharmony_ci return ret; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci reg = PHYUTMI_OTGDISABLE | 48762306a36Sopenharmony_ci PHYUTMI_FORCESUSPEND | 48862306a36Sopenharmony_ci PHYUTMI_FORCESLEEP; 48962306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* Resetting the PHYCLKRST enable bits to reduce leakage current */ 49262306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 49362306a36Sopenharmony_ci reg &= ~(PHYCLKRST_REF_SSP_EN | 49462306a36Sopenharmony_ci PHYCLKRST_SSC_EN | 49562306a36Sopenharmony_ci PHYCLKRST_COMMONONN); 49662306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Control PHYTEST to remove leakage current */ 49962306a36Sopenharmony_ci reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); 50062306a36Sopenharmony_ci reg |= PHYTEST_POWERDOWN_SSP | 50162306a36Sopenharmony_ci PHYTEST_POWERDOWN_HSP; 50262306a36Sopenharmony_ci writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->clk); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return 0; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int exynos5_usbdrd_phy_power_on(struct phy *phy) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci int ret; 51262306a36Sopenharmony_ci struct phy_usb_instance *inst = phy_get_drvdata(phy); 51362306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci clk_prepare_enable(phy_drd->ref_clk); 51862306a36Sopenharmony_ci if (!phy_drd->drv_data->has_common_clk_gate) { 51962306a36Sopenharmony_ci clk_prepare_enable(phy_drd->pipeclk); 52062306a36Sopenharmony_ci clk_prepare_enable(phy_drd->utmiclk); 52162306a36Sopenharmony_ci clk_prepare_enable(phy_drd->itpclk); 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Enable VBUS supply */ 52562306a36Sopenharmony_ci if (phy_drd->vbus_boost) { 52662306a36Sopenharmony_ci ret = regulator_enable(phy_drd->vbus_boost); 52762306a36Sopenharmony_ci if (ret) { 52862306a36Sopenharmony_ci dev_err(phy_drd->dev, 52962306a36Sopenharmony_ci "Failed to enable VBUS boost supply\n"); 53062306a36Sopenharmony_ci goto fail_vbus; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (phy_drd->vbus) { 53562306a36Sopenharmony_ci ret = regulator_enable(phy_drd->vbus); 53662306a36Sopenharmony_ci if (ret) { 53762306a36Sopenharmony_ci dev_err(phy_drd->dev, "Failed to enable VBUS supply\n"); 53862306a36Sopenharmony_ci goto fail_vbus_boost; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Power-on PHY*/ 54362306a36Sopenharmony_ci inst->phy_cfg->phy_isol(inst, 0); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci return 0; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cifail_vbus_boost: 54862306a36Sopenharmony_ci if (phy_drd->vbus_boost) 54962306a36Sopenharmony_ci regulator_disable(phy_drd->vbus_boost); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cifail_vbus: 55262306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->ref_clk); 55362306a36Sopenharmony_ci if (!phy_drd->drv_data->has_common_clk_gate) { 55462306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->itpclk); 55562306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->utmiclk); 55662306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->pipeclk); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci return ret; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int exynos5_usbdrd_phy_power_off(struct phy *phy) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct phy_usb_instance *inst = phy_get_drvdata(phy); 56562306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Power-off the PHY */ 57062306a36Sopenharmony_ci inst->phy_cfg->phy_isol(inst, 1); 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci /* Disable VBUS supply */ 57362306a36Sopenharmony_ci if (phy_drd->vbus) 57462306a36Sopenharmony_ci regulator_disable(phy_drd->vbus); 57562306a36Sopenharmony_ci if (phy_drd->vbus_boost) 57662306a36Sopenharmony_ci regulator_disable(phy_drd->vbus_boost); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->ref_clk); 57962306a36Sopenharmony_ci if (!phy_drd->drv_data->has_common_clk_gate) { 58062306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->itpclk); 58162306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->pipeclk); 58262306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->utmiclk); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return 0; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic int crport_handshake(struct exynos5_usbdrd_phy *phy_drd, 58962306a36Sopenharmony_ci u32 val, u32 cmd) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci unsigned int result; 59262306a36Sopenharmony_ci int err; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, 59762306a36Sopenharmony_ci result, (result & PHYREG1_CR_ACK), 1, 100); 59862306a36Sopenharmony_ci if (err == -ETIMEDOUT) { 59962306a36Sopenharmony_ci dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val); 60062306a36Sopenharmony_ci return err; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1, 60662306a36Sopenharmony_ci result, !(result & PHYREG1_CR_ACK), 1, 100); 60762306a36Sopenharmony_ci if (err == -ETIMEDOUT) { 60862306a36Sopenharmony_ci dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val); 60962306a36Sopenharmony_ci return err; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return 0; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int crport_ctrl_write(struct exynos5_usbdrd_phy *phy_drd, 61662306a36Sopenharmony_ci u32 addr, u32 data) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci int ret; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Write Address */ 62162306a36Sopenharmony_ci writel(PHYREG0_CR_DATA_IN(addr), 62262306a36Sopenharmony_ci phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); 62362306a36Sopenharmony_ci ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(addr), 62462306a36Sopenharmony_ci PHYREG0_CR_CAP_ADDR); 62562306a36Sopenharmony_ci if (ret) 62662306a36Sopenharmony_ci return ret; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Write Data */ 62962306a36Sopenharmony_ci writel(PHYREG0_CR_DATA_IN(data), 63062306a36Sopenharmony_ci phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0); 63162306a36Sopenharmony_ci ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data), 63262306a36Sopenharmony_ci PHYREG0_CR_CAP_DATA); 63362306a36Sopenharmony_ci if (ret) 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data), 63762306a36Sopenharmony_ci PHYREG0_CR_WRITE); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* 64362306a36Sopenharmony_ci * Calibrate few PHY parameters using CR_PORT register to meet 64462306a36Sopenharmony_ci * SuperSpeed requirements on Exynos5420 and Exynos5800 systems, 64562306a36Sopenharmony_ci * which have 28nm USB 3.0 DRD PHY. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_cistatic int exynos5420_usbdrd_phy_calibrate(struct exynos5_usbdrd_phy *phy_drd) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci unsigned int temp; 65062306a36Sopenharmony_ci int ret = 0; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci /* 65362306a36Sopenharmony_ci * Change los_bias to (0x5) for 28nm PHY from a 65462306a36Sopenharmony_ci * default value (0x0); los_level is set as default 65562306a36Sopenharmony_ci * (0x9) as also reflected in los_level[30:26] bits 65662306a36Sopenharmony_ci * of PHYPARAM0 register. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci temp = LOSLEVEL_OVRD_IN_LOS_BIAS_5420 | 65962306a36Sopenharmony_ci LOSLEVEL_OVRD_IN_EN | 66062306a36Sopenharmony_ci LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT; 66162306a36Sopenharmony_ci ret = crport_ctrl_write(phy_drd, 66262306a36Sopenharmony_ci EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN, 66362306a36Sopenharmony_ci temp); 66462306a36Sopenharmony_ci if (ret) { 66562306a36Sopenharmony_ci dev_err(phy_drd->dev, 66662306a36Sopenharmony_ci "Failed setting Loss-of-Signal level for SuperSpeed\n"); 66762306a36Sopenharmony_ci return ret; 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning, 67262306a36Sopenharmony_ci * to raise Tx signal level from its default value of (0x4) 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ci temp = TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420; 67562306a36Sopenharmony_ci ret = crport_ctrl_write(phy_drd, 67662306a36Sopenharmony_ci EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN, 67762306a36Sopenharmony_ci temp); 67862306a36Sopenharmony_ci if (ret) { 67962306a36Sopenharmony_ci dev_err(phy_drd->dev, 68062306a36Sopenharmony_ci "Failed setting Tx-Vboost-Level for SuperSpeed\n"); 68162306a36Sopenharmony_ci return ret; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * Set proper time to wait for RxDetect measurement, for 68662306a36Sopenharmony_ci * desired reference clock of PHY, by tuning the CR_PORT 68762306a36Sopenharmony_ci * register LANE0.TX_DEBUG which is internal to PHY. 68862306a36Sopenharmony_ci * This fixes issue with few USB 3.0 devices, which are 68962306a36Sopenharmony_ci * not detected (not even generate interrupts on the bus 69062306a36Sopenharmony_ci * on insertion) without this change. 69162306a36Sopenharmony_ci * e.g. Samsung SUM-TSB16S 3.0 USB drive. 69262306a36Sopenharmony_ci */ 69362306a36Sopenharmony_ci switch (phy_drd->extrefclk) { 69462306a36Sopenharmony_ci case EXYNOS5_FSEL_50MHZ: 69562306a36Sopenharmony_ci temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M; 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci case EXYNOS5_FSEL_20MHZ: 69862306a36Sopenharmony_ci case EXYNOS5_FSEL_19MHZ2: 69962306a36Sopenharmony_ci temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M; 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci case EXYNOS5_FSEL_24MHZ: 70262306a36Sopenharmony_ci default: 70362306a36Sopenharmony_ci temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M; 70462306a36Sopenharmony_ci break; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ret = crport_ctrl_write(phy_drd, 70862306a36Sopenharmony_ci EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG, 70962306a36Sopenharmony_ci temp); 71062306a36Sopenharmony_ci if (ret) 71162306a36Sopenharmony_ci dev_err(phy_drd->dev, 71262306a36Sopenharmony_ci "Fail to set RxDet measurement time for SuperSpeed\n"); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return ret; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic struct phy *exynos5_usbdrd_phy_xlate(struct device *dev, 71862306a36Sopenharmony_ci struct of_phandle_args *args) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM)) 72362306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return phy_drd->phys[args->args[0]].phy; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic int exynos5_usbdrd_phy_calibrate(struct phy *phy) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct phy_usb_instance *inst = phy_get_drvdata(phy); 73162306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) 73462306a36Sopenharmony_ci return exynos5420_usbdrd_phy_calibrate(phy_drd); 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic const struct phy_ops exynos5_usbdrd_phy_ops = { 73962306a36Sopenharmony_ci .init = exynos5_usbdrd_phy_init, 74062306a36Sopenharmony_ci .exit = exynos5_usbdrd_phy_exit, 74162306a36Sopenharmony_ci .power_on = exynos5_usbdrd_phy_power_on, 74262306a36Sopenharmony_ci .power_off = exynos5_usbdrd_phy_power_off, 74362306a36Sopenharmony_ci .calibrate = exynos5_usbdrd_phy_calibrate, 74462306a36Sopenharmony_ci .owner = THIS_MODULE, 74562306a36Sopenharmony_ci}; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic void exynos850_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci void __iomem *regs_base = phy_drd->reg_phy; 75062306a36Sopenharmony_ci u32 reg; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* 75362306a36Sopenharmony_ci * Disable HWACG (hardware auto clock gating control). This will force 75462306a36Sopenharmony_ci * QACTIVE signal in Q-Channel interface to HIGH level, to make sure 75562306a36Sopenharmony_ci * the PHY clock is not gated by the hardware. 75662306a36Sopenharmony_ci */ 75762306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); 75862306a36Sopenharmony_ci reg |= LINKCTRL_FORCE_QACT; 75962306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* Start PHY Reset (POR=high) */ 76262306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_CLKRST); 76362306a36Sopenharmony_ci reg |= CLKRST_PHY_SW_RST; 76462306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_CLKRST); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* Enable UTMI+ */ 76762306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_UTMI); 76862306a36Sopenharmony_ci reg &= ~(UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP | UTMI_DP_PULLDOWN | 76962306a36Sopenharmony_ci UTMI_DM_PULLDOWN); 77062306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_UTMI); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci /* Set PHY clock and control HS PHY */ 77362306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_HSP); 77462306a36Sopenharmony_ci reg |= HSP_EN_UTMISUSPEND | HSP_COMMONONN; 77562306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_HSP); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Set VBUS Valid and D+ pull-up control by VBUS pad usage */ 77862306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_LINKCTRL); 77962306a36Sopenharmony_ci reg |= LINKCTRL_BUS_FILTER_BYPASS(0xf); 78062306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_LINKCTRL); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_UTMI); 78362306a36Sopenharmony_ci reg |= UTMI_FORCE_BVALID | UTMI_FORCE_VBUSVALID; 78462306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_UTMI); 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_HSP); 78762306a36Sopenharmony_ci reg |= HSP_VBUSVLDEXT | HSP_VBUSVLDEXTSEL; 78862306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_HSP); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* Power up PHY analog blocks */ 79162306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); 79262306a36Sopenharmony_ci reg &= ~HSP_TEST_SIDDQ; 79362306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* Finish PHY reset (POR=low) */ 79662306a36Sopenharmony_ci udelay(10); /* required before doing POR=low */ 79762306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_CLKRST); 79862306a36Sopenharmony_ci reg &= ~(CLKRST_PHY_SW_RST | CLKRST_PORT_RST); 79962306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_CLKRST); 80062306a36Sopenharmony_ci udelay(75); /* required after POR=low for guaranteed PHY clock */ 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Disable single ended signal out */ 80362306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_HSP); 80462306a36Sopenharmony_ci reg &= ~HSP_FSV_OUT_EN; 80562306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_HSP); 80662306a36Sopenharmony_ci} 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic int exynos850_usbdrd_phy_init(struct phy *phy) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct phy_usb_instance *inst = phy_get_drvdata(phy); 81162306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 81262306a36Sopenharmony_ci int ret; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci ret = clk_prepare_enable(phy_drd->clk); 81562306a36Sopenharmony_ci if (ret) 81662306a36Sopenharmony_ci return ret; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* UTMI or PIPE3 specific init */ 81962306a36Sopenharmony_ci inst->phy_cfg->phy_init(phy_drd); 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->clk); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic int exynos850_usbdrd_phy_exit(struct phy *phy) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct phy_usb_instance *inst = phy_get_drvdata(phy); 82962306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); 83062306a36Sopenharmony_ci void __iomem *regs_base = phy_drd->reg_phy; 83162306a36Sopenharmony_ci u32 reg; 83262306a36Sopenharmony_ci int ret; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci ret = clk_prepare_enable(phy_drd->clk); 83562306a36Sopenharmony_ci if (ret) 83662306a36Sopenharmony_ci return ret; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci /* Set PHY clock and control HS PHY */ 83962306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_UTMI); 84062306a36Sopenharmony_ci reg &= ~(UTMI_DP_PULLDOWN | UTMI_DM_PULLDOWN); 84162306a36Sopenharmony_ci reg |= UTMI_FORCE_SUSPEND | UTMI_FORCE_SLEEP; 84262306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_UTMI); 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci /* Power down PHY analog blocks */ 84562306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_HSP_TEST); 84662306a36Sopenharmony_ci reg |= HSP_TEST_SIDDQ; 84762306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_HSP_TEST); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci /* Link reset */ 85062306a36Sopenharmony_ci reg = readl(regs_base + EXYNOS850_DRD_CLKRST); 85162306a36Sopenharmony_ci reg |= CLKRST_LINK_SW_RST; 85262306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_CLKRST); 85362306a36Sopenharmony_ci udelay(10); /* required before doing POR=low */ 85462306a36Sopenharmony_ci reg &= ~CLKRST_LINK_SW_RST; 85562306a36Sopenharmony_ci writel(reg, regs_base + EXYNOS850_DRD_CLKRST); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci clk_disable_unprepare(phy_drd->clk); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci return 0; 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic const struct phy_ops exynos850_usbdrd_phy_ops = { 86362306a36Sopenharmony_ci .init = exynos850_usbdrd_phy_init, 86462306a36Sopenharmony_ci .exit = exynos850_usbdrd_phy_exit, 86562306a36Sopenharmony_ci .power_on = exynos5_usbdrd_phy_power_on, 86662306a36Sopenharmony_ci .power_off = exynos5_usbdrd_phy_power_off, 86762306a36Sopenharmony_ci .owner = THIS_MODULE, 86862306a36Sopenharmony_ci}; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci unsigned long ref_rate; 87362306a36Sopenharmony_ci int ret; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci phy_drd->clk = devm_clk_get(phy_drd->dev, "phy"); 87662306a36Sopenharmony_ci if (IS_ERR(phy_drd->clk)) { 87762306a36Sopenharmony_ci dev_err(phy_drd->dev, "Failed to get phy clock\n"); 87862306a36Sopenharmony_ci return PTR_ERR(phy_drd->clk); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref"); 88262306a36Sopenharmony_ci if (IS_ERR(phy_drd->ref_clk)) { 88362306a36Sopenharmony_ci dev_err(phy_drd->dev, "Failed to get phy reference clock\n"); 88462306a36Sopenharmony_ci return PTR_ERR(phy_drd->ref_clk); 88562306a36Sopenharmony_ci } 88662306a36Sopenharmony_ci ref_rate = clk_get_rate(phy_drd->ref_clk); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk); 88962306a36Sopenharmony_ci if (ret) { 89062306a36Sopenharmony_ci dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n", 89162306a36Sopenharmony_ci ref_rate); 89262306a36Sopenharmony_ci return ret; 89362306a36Sopenharmony_ci } 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (!phy_drd->drv_data->has_common_clk_gate) { 89662306a36Sopenharmony_ci phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe"); 89762306a36Sopenharmony_ci if (IS_ERR(phy_drd->pipeclk)) { 89862306a36Sopenharmony_ci dev_info(phy_drd->dev, 89962306a36Sopenharmony_ci "PIPE3 phy operational clock not specified\n"); 90062306a36Sopenharmony_ci phy_drd->pipeclk = NULL; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi"); 90462306a36Sopenharmony_ci if (IS_ERR(phy_drd->utmiclk)) { 90562306a36Sopenharmony_ci dev_info(phy_drd->dev, 90662306a36Sopenharmony_ci "UTMI phy operational clock not specified\n"); 90762306a36Sopenharmony_ci phy_drd->utmiclk = NULL; 90862306a36Sopenharmony_ci } 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp"); 91162306a36Sopenharmony_ci if (IS_ERR(phy_drd->itpclk)) { 91262306a36Sopenharmony_ci dev_info(phy_drd->dev, 91362306a36Sopenharmony_ci "ITP clock from main OSC not specified\n"); 91462306a36Sopenharmony_ci phy_drd->itpclk = NULL; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = { 92262306a36Sopenharmony_ci { 92362306a36Sopenharmony_ci .id = EXYNOS5_DRDPHY_UTMI, 92462306a36Sopenharmony_ci .phy_isol = exynos5_usbdrd_phy_isol, 92562306a36Sopenharmony_ci .phy_init = exynos5_usbdrd_utmi_init, 92662306a36Sopenharmony_ci .set_refclk = exynos5_usbdrd_utmi_set_refclk, 92762306a36Sopenharmony_ci }, 92862306a36Sopenharmony_ci { 92962306a36Sopenharmony_ci .id = EXYNOS5_DRDPHY_PIPE3, 93062306a36Sopenharmony_ci .phy_isol = exynos5_usbdrd_phy_isol, 93162306a36Sopenharmony_ci .phy_init = exynos5_usbdrd_pipe3_init, 93262306a36Sopenharmony_ci .set_refclk = exynos5_usbdrd_pipe3_set_refclk, 93362306a36Sopenharmony_ci }, 93462306a36Sopenharmony_ci}; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_cistatic const struct exynos5_usbdrd_phy_config phy_cfg_exynos850[] = { 93762306a36Sopenharmony_ci { 93862306a36Sopenharmony_ci .id = EXYNOS5_DRDPHY_UTMI, 93962306a36Sopenharmony_ci .phy_isol = exynos5_usbdrd_phy_isol, 94062306a36Sopenharmony_ci .phy_init = exynos850_usbdrd_utmi_init, 94162306a36Sopenharmony_ci }, 94262306a36Sopenharmony_ci}; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_cistatic const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = { 94562306a36Sopenharmony_ci .phy_cfg = phy_cfg_exynos5, 94662306a36Sopenharmony_ci .phy_ops = &exynos5_usbdrd_phy_ops, 94762306a36Sopenharmony_ci .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, 94862306a36Sopenharmony_ci .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL, 94962306a36Sopenharmony_ci .has_common_clk_gate = true, 95062306a36Sopenharmony_ci}; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = { 95362306a36Sopenharmony_ci .phy_cfg = phy_cfg_exynos5, 95462306a36Sopenharmony_ci .phy_ops = &exynos5_usbdrd_phy_ops, 95562306a36Sopenharmony_ci .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, 95662306a36Sopenharmony_ci .has_common_clk_gate = true, 95762306a36Sopenharmony_ci}; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = { 96062306a36Sopenharmony_ci .phy_cfg = phy_cfg_exynos5, 96162306a36Sopenharmony_ci .phy_ops = &exynos5_usbdrd_phy_ops, 96262306a36Sopenharmony_ci .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, 96362306a36Sopenharmony_ci .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL, 96462306a36Sopenharmony_ci .has_common_clk_gate = false, 96562306a36Sopenharmony_ci}; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_cistatic const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = { 96862306a36Sopenharmony_ci .phy_cfg = phy_cfg_exynos5, 96962306a36Sopenharmony_ci .phy_ops = &exynos5_usbdrd_phy_ops, 97062306a36Sopenharmony_ci .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, 97162306a36Sopenharmony_ci .has_common_clk_gate = false, 97262306a36Sopenharmony_ci}; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic const struct exynos5_usbdrd_phy_drvdata exynos850_usbdrd_phy = { 97562306a36Sopenharmony_ci .phy_cfg = phy_cfg_exynos850, 97662306a36Sopenharmony_ci .phy_ops = &exynos850_usbdrd_phy_ops, 97762306a36Sopenharmony_ci .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL, 97862306a36Sopenharmony_ci .has_common_clk_gate = true, 97962306a36Sopenharmony_ci}; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic const struct of_device_id exynos5_usbdrd_phy_of_match[] = { 98262306a36Sopenharmony_ci { 98362306a36Sopenharmony_ci .compatible = "samsung,exynos5250-usbdrd-phy", 98462306a36Sopenharmony_ci .data = &exynos5250_usbdrd_phy 98562306a36Sopenharmony_ci }, { 98662306a36Sopenharmony_ci .compatible = "samsung,exynos5420-usbdrd-phy", 98762306a36Sopenharmony_ci .data = &exynos5420_usbdrd_phy 98862306a36Sopenharmony_ci }, { 98962306a36Sopenharmony_ci .compatible = "samsung,exynos5433-usbdrd-phy", 99062306a36Sopenharmony_ci .data = &exynos5433_usbdrd_phy 99162306a36Sopenharmony_ci }, { 99262306a36Sopenharmony_ci .compatible = "samsung,exynos7-usbdrd-phy", 99362306a36Sopenharmony_ci .data = &exynos7_usbdrd_phy 99462306a36Sopenharmony_ci }, { 99562306a36Sopenharmony_ci .compatible = "samsung,exynos850-usbdrd-phy", 99662306a36Sopenharmony_ci .data = &exynos850_usbdrd_phy 99762306a36Sopenharmony_ci }, 99862306a36Sopenharmony_ci { }, 99962306a36Sopenharmony_ci}; 100062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic int exynos5_usbdrd_phy_probe(struct platform_device *pdev) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 100562306a36Sopenharmony_ci struct device_node *node = dev->of_node; 100662306a36Sopenharmony_ci struct exynos5_usbdrd_phy *phy_drd; 100762306a36Sopenharmony_ci struct phy_provider *phy_provider; 100862306a36Sopenharmony_ci const struct exynos5_usbdrd_phy_drvdata *drv_data; 100962306a36Sopenharmony_ci struct regmap *reg_pmu; 101062306a36Sopenharmony_ci u32 pmu_offset; 101162306a36Sopenharmony_ci int i, ret; 101262306a36Sopenharmony_ci int channel; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL); 101562306a36Sopenharmony_ci if (!phy_drd) 101662306a36Sopenharmony_ci return -ENOMEM; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci dev_set_drvdata(dev, phy_drd); 101962306a36Sopenharmony_ci phy_drd->dev = dev; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, 0); 102262306a36Sopenharmony_ci if (IS_ERR(phy_drd->reg_phy)) 102362306a36Sopenharmony_ci return PTR_ERR(phy_drd->reg_phy); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci drv_data = of_device_get_match_data(dev); 102662306a36Sopenharmony_ci if (!drv_data) 102762306a36Sopenharmony_ci return -EINVAL; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci phy_drd->drv_data = drv_data; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci ret = exynos5_usbdrd_phy_clk_handle(phy_drd); 103262306a36Sopenharmony_ci if (ret) { 103362306a36Sopenharmony_ci dev_err(dev, "Failed to initialize clocks\n"); 103462306a36Sopenharmony_ci return ret; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node, 103862306a36Sopenharmony_ci "samsung,pmu-syscon"); 103962306a36Sopenharmony_ci if (IS_ERR(reg_pmu)) { 104062306a36Sopenharmony_ci dev_err(dev, "Failed to lookup PMU regmap\n"); 104162306a36Sopenharmony_ci return PTR_ERR(reg_pmu); 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* 104562306a36Sopenharmony_ci * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with 104662306a36Sopenharmony_ci * each having separate power control registers. 104762306a36Sopenharmony_ci * 'channel' facilitates to set such registers. 104862306a36Sopenharmony_ci */ 104962306a36Sopenharmony_ci channel = of_alias_get_id(node, "usbdrdphy"); 105062306a36Sopenharmony_ci if (channel < 0) 105162306a36Sopenharmony_ci dev_dbg(dev, "Not a multi-controller usbdrd phy\n"); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci switch (channel) { 105462306a36Sopenharmony_ci case 1: 105562306a36Sopenharmony_ci pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy; 105662306a36Sopenharmony_ci break; 105762306a36Sopenharmony_ci case 0: 105862306a36Sopenharmony_ci default: 105962306a36Sopenharmony_ci pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy; 106062306a36Sopenharmony_ci break; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci /* Get Vbus regulators */ 106462306a36Sopenharmony_ci phy_drd->vbus = devm_regulator_get(dev, "vbus"); 106562306a36Sopenharmony_ci if (IS_ERR(phy_drd->vbus)) { 106662306a36Sopenharmony_ci ret = PTR_ERR(phy_drd->vbus); 106762306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 106862306a36Sopenharmony_ci return ret; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci dev_warn(dev, "Failed to get VBUS supply regulator\n"); 107162306a36Sopenharmony_ci phy_drd->vbus = NULL; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost"); 107562306a36Sopenharmony_ci if (IS_ERR(phy_drd->vbus_boost)) { 107662306a36Sopenharmony_ci ret = PTR_ERR(phy_drd->vbus_boost); 107762306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 107862306a36Sopenharmony_ci return ret; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci dev_warn(dev, "Failed to get VBUS boost supply regulator\n"); 108162306a36Sopenharmony_ci phy_drd->vbus_boost = NULL; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci dev_vdbg(dev, "Creating usbdrd_phy phy\n"); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) { 108762306a36Sopenharmony_ci struct phy *phy = devm_phy_create(dev, NULL, drv_data->phy_ops); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (IS_ERR(phy)) { 109062306a36Sopenharmony_ci dev_err(dev, "Failed to create usbdrd_phy phy\n"); 109162306a36Sopenharmony_ci return PTR_ERR(phy); 109262306a36Sopenharmony_ci } 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci phy_drd->phys[i].phy = phy; 109562306a36Sopenharmony_ci phy_drd->phys[i].index = i; 109662306a36Sopenharmony_ci phy_drd->phys[i].reg_pmu = reg_pmu; 109762306a36Sopenharmony_ci phy_drd->phys[i].pmu_offset = pmu_offset; 109862306a36Sopenharmony_ci phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i]; 109962306a36Sopenharmony_ci phy_set_drvdata(phy, &phy_drd->phys[i]); 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, 110362306a36Sopenharmony_ci exynos5_usbdrd_phy_xlate); 110462306a36Sopenharmony_ci if (IS_ERR(phy_provider)) { 110562306a36Sopenharmony_ci dev_err(phy_drd->dev, "Failed to register phy provider\n"); 110662306a36Sopenharmony_ci return PTR_ERR(phy_provider); 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic struct platform_driver exynos5_usb3drd_phy = { 111362306a36Sopenharmony_ci .probe = exynos5_usbdrd_phy_probe, 111462306a36Sopenharmony_ci .driver = { 111562306a36Sopenharmony_ci .of_match_table = exynos5_usbdrd_phy_of_match, 111662306a36Sopenharmony_ci .name = "exynos5_usb3drd_phy", 111762306a36Sopenharmony_ci .suppress_bind_attrs = true, 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci}; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cimodule_platform_driver(exynos5_usb3drd_phy); 112262306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver"); 112362306a36Sopenharmony_ciMODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>"); 112462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 112562306a36Sopenharmony_ciMODULE_ALIAS("platform:exynos5_usb3drd_phy"); 1126