cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

phy-exynos5-usbdrd.c (26069B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Samsung Exynos5 SoC series USB DRD PHY driver
      4 *
      5 * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series
      6 *
      7 * Copyright (C) 2014 Samsung Electronics Co., Ltd.
      8 * Author: Vivek Gautam <gautam.vivek@samsung.com>
      9 */
     10
     11#include <linux/clk.h>
     12#include <linux/delay.h>
     13#include <linux/io.h>
     14#include <linux/kernel.h>
     15#include <linux/module.h>
     16#include <linux/of.h>
     17#include <linux/of_address.h>
     18#include <linux/of_device.h>
     19#include <linux/iopoll.h>
     20#include <linux/phy/phy.h>
     21#include <linux/platform_device.h>
     22#include <linux/mutex.h>
     23#include <linux/mfd/syscon.h>
     24#include <linux/regmap.h>
     25#include <linux/regulator/consumer.h>
     26#include <linux/soc/samsung/exynos-regs-pmu.h>
     27
     28/* Exynos USB PHY registers */
     29#define EXYNOS5_FSEL_9MHZ6		0x0
     30#define EXYNOS5_FSEL_10MHZ		0x1
     31#define EXYNOS5_FSEL_12MHZ		0x2
     32#define EXYNOS5_FSEL_19MHZ2		0x3
     33#define EXYNOS5_FSEL_20MHZ		0x4
     34#define EXYNOS5_FSEL_24MHZ		0x5
     35#define EXYNOS5_FSEL_50MHZ		0x7
     36
     37/* Exynos5: USB 3.0 DRD PHY registers */
     38#define EXYNOS5_DRD_LINKSYSTEM			0x04
     39
     40#define LINKSYSTEM_FLADJ_MASK			(0x3f << 1)
     41#define LINKSYSTEM_FLADJ(_x)			((_x) << 1)
     42#define LINKSYSTEM_XHCI_VERSION_CONTROL		BIT(27)
     43
     44#define EXYNOS5_DRD_PHYUTMI			0x08
     45
     46#define PHYUTMI_OTGDISABLE			BIT(6)
     47#define PHYUTMI_FORCESUSPEND			BIT(1)
     48#define PHYUTMI_FORCESLEEP			BIT(0)
     49
     50#define EXYNOS5_DRD_PHYPIPE			0x0c
     51
     52#define EXYNOS5_DRD_PHYCLKRST			0x10
     53
     54#define PHYCLKRST_EN_UTMISUSPEND		BIT(31)
     55
     56#define PHYCLKRST_SSC_REFCLKSEL_MASK		(0xff << 23)
     57#define PHYCLKRST_SSC_REFCLKSEL(_x)		((_x) << 23)
     58
     59#define PHYCLKRST_SSC_RANGE_MASK		(0x03 << 21)
     60#define PHYCLKRST_SSC_RANGE(_x)			((_x) << 21)
     61
     62#define PHYCLKRST_SSC_EN			BIT(20)
     63#define PHYCLKRST_REF_SSP_EN			BIT(19)
     64#define PHYCLKRST_REF_CLKDIV2			BIT(18)
     65
     66#define PHYCLKRST_MPLL_MULTIPLIER_MASK		(0x7f << 11)
     67#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF	(0x19 << 11)
     68#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF	(0x32 << 11)
     69#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF	(0x68 << 11)
     70#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF	(0x7d << 11)
     71#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF	(0x02 << 11)
     72
     73#define PHYCLKRST_FSEL_UTMI_MASK		(0x7 << 5)
     74#define PHYCLKRST_FSEL_PIPE_MASK		(0x7 << 8)
     75#define PHYCLKRST_FSEL(_x)			((_x) << 5)
     76#define PHYCLKRST_FSEL_PAD_100MHZ		(0x27 << 5)
     77#define PHYCLKRST_FSEL_PAD_24MHZ		(0x2a << 5)
     78#define PHYCLKRST_FSEL_PAD_20MHZ		(0x31 << 5)
     79#define PHYCLKRST_FSEL_PAD_19_2MHZ		(0x38 << 5)
     80
     81#define PHYCLKRST_RETENABLEN			BIT(4)
     82
     83#define PHYCLKRST_REFCLKSEL_MASK		(0x03 << 2)
     84#define PHYCLKRST_REFCLKSEL_PAD_REFCLK		(0x2 << 2)
     85#define PHYCLKRST_REFCLKSEL_EXT_REFCLK		(0x3 << 2)
     86
     87#define PHYCLKRST_PORTRESET			BIT(1)
     88#define PHYCLKRST_COMMONONN			BIT(0)
     89
     90#define EXYNOS5_DRD_PHYREG0			0x14
     91#define PHYREG0_SSC_REF_CLK_SEL			BIT(21)
     92#define PHYREG0_SSC_RANGE			BIT(20)
     93#define PHYREG0_CR_WRITE			BIT(19)
     94#define PHYREG0_CR_READ				BIT(18)
     95#define PHYREG0_CR_DATA_IN(_x)			((_x) << 2)
     96#define PHYREG0_CR_CAP_DATA			BIT(1)
     97#define PHYREG0_CR_CAP_ADDR			BIT(0)
     98
     99#define EXYNOS5_DRD_PHYREG1			0x18
    100#define PHYREG1_CR_DATA_OUT(_x)			((_x) << 1)
    101#define PHYREG1_CR_ACK				BIT(0)
    102
    103#define EXYNOS5_DRD_PHYPARAM0			0x1c
    104
    105#define PHYPARAM0_REF_USE_PAD			BIT(31)
    106#define PHYPARAM0_REF_LOSLEVEL_MASK		(0x1f << 26)
    107#define PHYPARAM0_REF_LOSLEVEL			(0x9 << 26)
    108
    109#define EXYNOS5_DRD_PHYPARAM1			0x20
    110
    111#define PHYPARAM1_PCS_TXDEEMPH_MASK		(0x1f << 0)
    112#define PHYPARAM1_PCS_TXDEEMPH			(0x1c)
    113
    114#define EXYNOS5_DRD_PHYTERM			0x24
    115
    116#define EXYNOS5_DRD_PHYTEST			0x28
    117
    118#define PHYTEST_POWERDOWN_SSP			BIT(3)
    119#define PHYTEST_POWERDOWN_HSP			BIT(2)
    120
    121#define EXYNOS5_DRD_PHYADP			0x2c
    122
    123#define EXYNOS5_DRD_PHYUTMICLKSEL		0x30
    124
    125#define PHYUTMICLKSEL_UTMI_CLKSEL		BIT(2)
    126
    127#define EXYNOS5_DRD_PHYRESUME			0x34
    128#define EXYNOS5_DRD_LINKPORT			0x44
    129
    130/* USB 3.0 DRD PHY SS Function Control Reg; accessed by CR_PORT */
    131#define EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN		(0x15)
    132#define LOSLEVEL_OVRD_IN_LOS_BIAS_5420			(0x5 << 13)
    133#define LOSLEVEL_OVRD_IN_LOS_BIAS_DEFAULT		(0x0 << 13)
    134#define LOSLEVEL_OVRD_IN_EN				(0x1 << 10)
    135#define LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT		(0x9 << 0)
    136
    137#define EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN	(0x12)
    138#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420		(0x5 << 13)
    139#define TX_VBOOSTLEVEL_OVRD_IN_VBOOST_DEFAULT		(0x4 << 13)
    140
    141#define EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG		(0x1010)
    142#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M		(0x4 << 4)
    143#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M		(0x8 << 4)
    144#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_25M_26M		(0x8 << 4)
    145#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M	(0x20 << 4)
    146#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_62M5		(0x20 << 4)
    147#define LANE0_TX_DEBUG_RXDET_MEAS_TIME_96M_100M		(0x40 << 4)
    148
    149#define KHZ	1000
    150#define MHZ	(KHZ * KHZ)
    151
    152enum exynos5_usbdrd_phy_id {
    153	EXYNOS5_DRDPHY_UTMI,
    154	EXYNOS5_DRDPHY_PIPE3,
    155	EXYNOS5_DRDPHYS_NUM,
    156};
    157
    158struct phy_usb_instance;
    159struct exynos5_usbdrd_phy;
    160
    161struct exynos5_usbdrd_phy_config {
    162	u32 id;
    163	void (*phy_isol)(struct phy_usb_instance *inst, u32 on);
    164	void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd);
    165	unsigned int (*set_refclk)(struct phy_usb_instance *inst);
    166};
    167
    168struct exynos5_usbdrd_phy_drvdata {
    169	const struct exynos5_usbdrd_phy_config *phy_cfg;
    170	u32 pmu_offset_usbdrd0_phy;
    171	u32 pmu_offset_usbdrd1_phy;
    172	bool has_common_clk_gate;
    173};
    174
    175/**
    176 * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY
    177 * @dev: pointer to device instance of this platform device
    178 * @reg_phy: usb phy controller register memory base
    179 * @clk: phy clock for register access
    180 * @pipeclk: clock for pipe3 phy
    181 * @utmiclk: clock for utmi+ phy
    182 * @itpclk: clock for ITP generation
    183 * @drv_data: pointer to SoC level driver data structure
    184 * @phys: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
    185 *	    instances each with its 'phy' and 'phy_cfg'.
    186 * @extrefclk: frequency select settings when using 'separate
    187 *	       reference clocks' for SS and HS operations
    188 * @ref_clk: reference clock to PHY block from which PHY's
    189 *	     operational clocks are derived
    190 * @vbus: VBUS regulator for phy
    191 * @vbus_boost: Boost regulator for VBUS present on few Exynos boards
    192 */
    193struct exynos5_usbdrd_phy {
    194	struct device *dev;
    195	void __iomem *reg_phy;
    196	struct clk *clk;
    197	struct clk *pipeclk;
    198	struct clk *utmiclk;
    199	struct clk *itpclk;
    200	const struct exynos5_usbdrd_phy_drvdata *drv_data;
    201	struct phy_usb_instance {
    202		struct phy *phy;
    203		u32 index;
    204		struct regmap *reg_pmu;
    205		u32 pmu_offset;
    206		const struct exynos5_usbdrd_phy_config *phy_cfg;
    207	} phys[EXYNOS5_DRDPHYS_NUM];
    208	u32 extrefclk;
    209	struct clk *ref_clk;
    210	struct regulator *vbus;
    211	struct regulator *vbus_boost;
    212};
    213
    214static inline
    215struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst)
    216{
    217	return container_of((inst), struct exynos5_usbdrd_phy,
    218			    phys[(inst)->index]);
    219}
    220
    221/*
    222 * exynos5_rate_to_clk() converts the supplied clock rate to the value that
    223 * can be written to the phy register.
    224 */
    225static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg)
    226{
    227	/* EXYNOS5_FSEL_MASK */
    228
    229	switch (rate) {
    230	case 9600 * KHZ:
    231		*reg = EXYNOS5_FSEL_9MHZ6;
    232		break;
    233	case 10 * MHZ:
    234		*reg = EXYNOS5_FSEL_10MHZ;
    235		break;
    236	case 12 * MHZ:
    237		*reg = EXYNOS5_FSEL_12MHZ;
    238		break;
    239	case 19200 * KHZ:
    240		*reg = EXYNOS5_FSEL_19MHZ2;
    241		break;
    242	case 20 * MHZ:
    243		*reg = EXYNOS5_FSEL_20MHZ;
    244		break;
    245	case 24 * MHZ:
    246		*reg = EXYNOS5_FSEL_24MHZ;
    247		break;
    248	case 50 * MHZ:
    249		*reg = EXYNOS5_FSEL_50MHZ;
    250		break;
    251	default:
    252		return -EINVAL;
    253	}
    254
    255	return 0;
    256}
    257
    258static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst,
    259						unsigned int on)
    260{
    261	unsigned int val;
    262
    263	if (!inst->reg_pmu)
    264		return;
    265
    266	val = on ? 0 : EXYNOS4_PHY_ENABLE;
    267
    268	regmap_update_bits(inst->reg_pmu, inst->pmu_offset,
    269			   EXYNOS4_PHY_ENABLE, val);
    270}
    271
    272/*
    273 * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock
    274 * from clock core. Further sets multiplier values and spread spectrum
    275 * clock settings for SuperSpeed operations.
    276 */
    277static unsigned int
    278exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
    279{
    280	u32 reg;
    281	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
    282
    283	/* restore any previous reference clock settings */
    284	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
    285
    286	/* Use EXTREFCLK as ref clock */
    287	reg &= ~PHYCLKRST_REFCLKSEL_MASK;
    288	reg |=	PHYCLKRST_REFCLKSEL_EXT_REFCLK;
    289
    290	/* FSEL settings corresponding to reference clock */
    291	reg &= ~PHYCLKRST_FSEL_PIPE_MASK |
    292		PHYCLKRST_MPLL_MULTIPLIER_MASK |
    293		PHYCLKRST_SSC_REFCLKSEL_MASK;
    294	switch (phy_drd->extrefclk) {
    295	case EXYNOS5_FSEL_50MHZ:
    296		reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
    297			PHYCLKRST_SSC_REFCLKSEL(0x00));
    298		break;
    299	case EXYNOS5_FSEL_24MHZ:
    300		reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
    301			PHYCLKRST_SSC_REFCLKSEL(0x88));
    302		break;
    303	case EXYNOS5_FSEL_20MHZ:
    304		reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
    305			PHYCLKRST_SSC_REFCLKSEL(0x00));
    306		break;
    307	case EXYNOS5_FSEL_19MHZ2:
    308		reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
    309			PHYCLKRST_SSC_REFCLKSEL(0x88));
    310		break;
    311	default:
    312		dev_dbg(phy_drd->dev, "unsupported ref clk\n");
    313		break;
    314	}
    315
    316	return reg;
    317}
    318
    319/*
    320 * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock
    321 * from clock core. Further sets the FSEL values for HighSpeed operations.
    322 */
    323static unsigned int
    324exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst)
    325{
    326	u32 reg;
    327	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
    328
    329	/* restore any previous reference clock settings */
    330	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
    331
    332	reg &= ~PHYCLKRST_REFCLKSEL_MASK;
    333	reg |=	PHYCLKRST_REFCLKSEL_EXT_REFCLK;
    334
    335	reg &= ~PHYCLKRST_FSEL_UTMI_MASK |
    336		PHYCLKRST_MPLL_MULTIPLIER_MASK |
    337		PHYCLKRST_SSC_REFCLKSEL_MASK;
    338	reg |= PHYCLKRST_FSEL(phy_drd->extrefclk);
    339
    340	return reg;
    341}
    342
    343static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
    344{
    345	u32 reg;
    346
    347	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
    348	/* Set Tx De-Emphasis level */
    349	reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
    350	reg |=	PHYPARAM1_PCS_TXDEEMPH;
    351	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
    352
    353	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
    354	reg &= ~PHYTEST_POWERDOWN_SSP;
    355	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
    356}
    357
    358static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
    359{
    360	u32 reg;
    361
    362	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
    363	/* Set Loss-of-Signal Detector sensitivity */
    364	reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK;
    365	reg |=	PHYPARAM0_REF_LOSLEVEL;
    366	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
    367
    368	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
    369	/* Set Tx De-Emphasis level */
    370	reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
    371	reg |=	PHYPARAM1_PCS_TXDEEMPH;
    372	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
    373
    374	/* UTMI Power Control */
    375	writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI);
    376
    377	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
    378	reg &= ~PHYTEST_POWERDOWN_HSP;
    379	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
    380}
    381
    382static int exynos5_usbdrd_phy_init(struct phy *phy)
    383{
    384	int ret;
    385	u32 reg;
    386	struct phy_usb_instance *inst = phy_get_drvdata(phy);
    387	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
    388
    389	ret = clk_prepare_enable(phy_drd->clk);
    390	if (ret)
    391		return ret;
    392
    393	/* Reset USB 3.0 PHY */
    394	writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
    395	writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME);
    396
    397	/*
    398	 * Setting the Frame length Adj value[6:1] to default 0x20
    399	 * See xHCI 1.0 spec, 5.2.4
    400	 */
    401	reg =	LINKSYSTEM_XHCI_VERSION_CONTROL |
    402		LINKSYSTEM_FLADJ(0x20);
    403	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM);
    404
    405	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
    406	/* Select PHY CLK source */
    407	reg &= ~PHYPARAM0_REF_USE_PAD;
    408	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
    409
    410	/* This bit must be set for both HS and SS operations */
    411	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL);
    412	reg |= PHYUTMICLKSEL_UTMI_CLKSEL;
    413	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL);
    414
    415	/* UTMI or PIPE3 specific init */
    416	inst->phy_cfg->phy_init(phy_drd);
    417
    418	/* reference clock settings */
    419	reg = inst->phy_cfg->set_refclk(inst);
    420
    421		/* Digital power supply in normal operating mode */
    422	reg |=	PHYCLKRST_RETENABLEN |
    423		/* Enable ref clock for SS function */
    424		PHYCLKRST_REF_SSP_EN |
    425		/* Enable spread spectrum */
    426		PHYCLKRST_SSC_EN |
    427		/* Power down HS Bias and PLL blocks in suspend mode */
    428		PHYCLKRST_COMMONONN |
    429		/* Reset the port */
    430		PHYCLKRST_PORTRESET;
    431
    432	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
    433
    434	udelay(10);
    435
    436	reg &= ~PHYCLKRST_PORTRESET;
    437	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
    438
    439	clk_disable_unprepare(phy_drd->clk);
    440
    441	return 0;
    442}
    443
    444static int exynos5_usbdrd_phy_exit(struct phy *phy)
    445{
    446	int ret;
    447	u32 reg;
    448	struct phy_usb_instance *inst = phy_get_drvdata(phy);
    449	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
    450
    451	ret = clk_prepare_enable(phy_drd->clk);
    452	if (ret)
    453		return ret;
    454
    455	reg =	PHYUTMI_OTGDISABLE |
    456		PHYUTMI_FORCESUSPEND |
    457		PHYUTMI_FORCESLEEP;
    458	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI);
    459
    460	/* Resetting the PHYCLKRST enable bits to reduce leakage current */
    461	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
    462	reg &= ~(PHYCLKRST_REF_SSP_EN |
    463		 PHYCLKRST_SSC_EN |
    464		 PHYCLKRST_COMMONONN);
    465	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
    466
    467	/* Control PHYTEST to remove leakage current */
    468	reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
    469	reg |=	PHYTEST_POWERDOWN_SSP |
    470		PHYTEST_POWERDOWN_HSP;
    471	writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
    472
    473	clk_disable_unprepare(phy_drd->clk);
    474
    475	return 0;
    476}
    477
    478static int exynos5_usbdrd_phy_power_on(struct phy *phy)
    479{
    480	int ret;
    481	struct phy_usb_instance *inst = phy_get_drvdata(phy);
    482	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
    483
    484	dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
    485
    486	clk_prepare_enable(phy_drd->ref_clk);
    487	if (!phy_drd->drv_data->has_common_clk_gate) {
    488		clk_prepare_enable(phy_drd->pipeclk);
    489		clk_prepare_enable(phy_drd->utmiclk);
    490		clk_prepare_enable(phy_drd->itpclk);
    491	}
    492
    493	/* Enable VBUS supply */
    494	if (phy_drd->vbus_boost) {
    495		ret = regulator_enable(phy_drd->vbus_boost);
    496		if (ret) {
    497			dev_err(phy_drd->dev,
    498				"Failed to enable VBUS boost supply\n");
    499			goto fail_vbus;
    500		}
    501	}
    502
    503	if (phy_drd->vbus) {
    504		ret = regulator_enable(phy_drd->vbus);
    505		if (ret) {
    506			dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
    507			goto fail_vbus_boost;
    508		}
    509	}
    510
    511	/* Power-on PHY*/
    512	inst->phy_cfg->phy_isol(inst, 0);
    513
    514	return 0;
    515
    516fail_vbus_boost:
    517	if (phy_drd->vbus_boost)
    518		regulator_disable(phy_drd->vbus_boost);
    519
    520fail_vbus:
    521	clk_disable_unprepare(phy_drd->ref_clk);
    522	if (!phy_drd->drv_data->has_common_clk_gate) {
    523		clk_disable_unprepare(phy_drd->itpclk);
    524		clk_disable_unprepare(phy_drd->utmiclk);
    525		clk_disable_unprepare(phy_drd->pipeclk);
    526	}
    527
    528	return ret;
    529}
    530
    531static int exynos5_usbdrd_phy_power_off(struct phy *phy)
    532{
    533	struct phy_usb_instance *inst = phy_get_drvdata(phy);
    534	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
    535
    536	dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n");
    537
    538	/* Power-off the PHY */
    539	inst->phy_cfg->phy_isol(inst, 1);
    540
    541	/* Disable VBUS supply */
    542	if (phy_drd->vbus)
    543		regulator_disable(phy_drd->vbus);
    544	if (phy_drd->vbus_boost)
    545		regulator_disable(phy_drd->vbus_boost);
    546
    547	clk_disable_unprepare(phy_drd->ref_clk);
    548	if (!phy_drd->drv_data->has_common_clk_gate) {
    549		clk_disable_unprepare(phy_drd->itpclk);
    550		clk_disable_unprepare(phy_drd->pipeclk);
    551		clk_disable_unprepare(phy_drd->utmiclk);
    552	}
    553
    554	return 0;
    555}
    556
    557static int crport_handshake(struct exynos5_usbdrd_phy *phy_drd,
    558			    u32 val, u32 cmd)
    559{
    560	unsigned int result;
    561	int err;
    562
    563	writel(val | cmd, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
    564
    565	err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
    566				 result, (result & PHYREG1_CR_ACK), 1, 100);
    567	if (err == -ETIMEDOUT) {
    568		dev_err(phy_drd->dev, "CRPORT handshake timeout1 (0x%08x)\n", val);
    569		return err;
    570	}
    571
    572	writel(val, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
    573
    574	err = readl_poll_timeout(phy_drd->reg_phy + EXYNOS5_DRD_PHYREG1,
    575				 result, !(result & PHYREG1_CR_ACK), 1, 100);
    576	if (err == -ETIMEDOUT) {
    577		dev_err(phy_drd->dev, "CRPORT handshake timeout2 (0x%08x)\n", val);
    578		return err;
    579	}
    580
    581	return 0;
    582}
    583
    584static int crport_ctrl_write(struct exynos5_usbdrd_phy *phy_drd,
    585			     u32 addr, u32 data)
    586{
    587	int ret;
    588
    589	/* Write Address */
    590	writel(PHYREG0_CR_DATA_IN(addr),
    591	       phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
    592	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(addr),
    593			       PHYREG0_CR_CAP_ADDR);
    594	if (ret)
    595		return ret;
    596
    597	/* Write Data */
    598	writel(PHYREG0_CR_DATA_IN(data),
    599	       phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
    600	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
    601			       PHYREG0_CR_CAP_DATA);
    602	if (ret)
    603		return ret;
    604
    605	ret = crport_handshake(phy_drd, PHYREG0_CR_DATA_IN(data),
    606			       PHYREG0_CR_WRITE);
    607
    608	return ret;
    609}
    610
    611/*
    612 * Calibrate few PHY parameters using CR_PORT register to meet
    613 * SuperSpeed requirements on Exynos5420 and Exynos5800 systems,
    614 * which have 28nm USB 3.0 DRD PHY.
    615 */
    616static int exynos5420_usbdrd_phy_calibrate(struct exynos5_usbdrd_phy *phy_drd)
    617{
    618	unsigned int temp;
    619	int ret = 0;
    620
    621	/*
    622	 * Change los_bias to (0x5) for 28nm PHY from a
    623	 * default value (0x0); los_level is set as default
    624	 * (0x9) as also reflected in los_level[30:26] bits
    625	 * of PHYPARAM0 register.
    626	 */
    627	temp = LOSLEVEL_OVRD_IN_LOS_BIAS_5420 |
    628		LOSLEVEL_OVRD_IN_EN |
    629		LOSLEVEL_OVRD_IN_LOS_LEVEL_DEFAULT;
    630	ret = crport_ctrl_write(phy_drd,
    631				EXYNOS5_DRD_PHYSS_LOSLEVEL_OVRD_IN,
    632				temp);
    633	if (ret) {
    634		dev_err(phy_drd->dev,
    635			"Failed setting Loss-of-Signal level for SuperSpeed\n");
    636		return ret;
    637	}
    638
    639	/*
    640	 * Set tx_vboost_lvl to (0x5) for 28nm PHY Tuning,
    641	 * to raise Tx signal level from its default value of (0x4)
    642	 */
    643	temp = TX_VBOOSTLEVEL_OVRD_IN_VBOOST_5420;
    644	ret = crport_ctrl_write(phy_drd,
    645				EXYNOS5_DRD_PHYSS_TX_VBOOSTLEVEL_OVRD_IN,
    646				temp);
    647	if (ret) {
    648		dev_err(phy_drd->dev,
    649			"Failed setting Tx-Vboost-Level for SuperSpeed\n");
    650		return ret;
    651	}
    652
    653	/*
    654	 * Set proper time to wait for RxDetect measurement, for
    655	 * desired reference clock of PHY, by tuning the CR_PORT
    656	 * register LANE0.TX_DEBUG which is internal to PHY.
    657	 * This fixes issue with few USB 3.0 devices, which are
    658	 * not detected (not even generate interrupts on the bus
    659	 * on insertion) without this change.
    660	 * e.g. Samsung SUM-TSB16S 3.0 USB drive.
    661	 */
    662	switch (phy_drd->extrefclk) {
    663	case EXYNOS5_FSEL_50MHZ:
    664		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_48M_50M_52M;
    665		break;
    666	case EXYNOS5_FSEL_20MHZ:
    667	case EXYNOS5_FSEL_19MHZ2:
    668		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_19M2_20M;
    669		break;
    670	case EXYNOS5_FSEL_24MHZ:
    671	default:
    672		temp = LANE0_TX_DEBUG_RXDET_MEAS_TIME_24M;
    673		break;
    674	}
    675
    676	ret = crport_ctrl_write(phy_drd,
    677				EXYNOS5_DRD_PHYSS_LANE0_TX_DEBUG,
    678				temp);
    679	if (ret)
    680		dev_err(phy_drd->dev,
    681			"Fail to set RxDet measurement time for SuperSpeed\n");
    682
    683	return ret;
    684}
    685
    686static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
    687					struct of_phandle_args *args)
    688{
    689	struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev);
    690
    691	if (WARN_ON(args->args[0] >= EXYNOS5_DRDPHYS_NUM))
    692		return ERR_PTR(-ENODEV);
    693
    694	return phy_drd->phys[args->args[0]].phy;
    695}
    696
    697static int exynos5_usbdrd_phy_calibrate(struct phy *phy)
    698{
    699	struct phy_usb_instance *inst = phy_get_drvdata(phy);
    700	struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
    701
    702	if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI)
    703		return exynos5420_usbdrd_phy_calibrate(phy_drd);
    704	return 0;
    705}
    706
    707static const struct phy_ops exynos5_usbdrd_phy_ops = {
    708	.init		= exynos5_usbdrd_phy_init,
    709	.exit		= exynos5_usbdrd_phy_exit,
    710	.power_on	= exynos5_usbdrd_phy_power_on,
    711	.power_off	= exynos5_usbdrd_phy_power_off,
    712	.calibrate	= exynos5_usbdrd_phy_calibrate,
    713	.owner		= THIS_MODULE,
    714};
    715
    716static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
    717{
    718	unsigned long ref_rate;
    719	int ret;
    720
    721	phy_drd->clk = devm_clk_get(phy_drd->dev, "phy");
    722	if (IS_ERR(phy_drd->clk)) {
    723		dev_err(phy_drd->dev, "Failed to get phy clock\n");
    724		return PTR_ERR(phy_drd->clk);
    725	}
    726
    727	phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref");
    728	if (IS_ERR(phy_drd->ref_clk)) {
    729		dev_err(phy_drd->dev, "Failed to get phy reference clock\n");
    730		return PTR_ERR(phy_drd->ref_clk);
    731	}
    732	ref_rate = clk_get_rate(phy_drd->ref_clk);
    733
    734	ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
    735	if (ret) {
    736		dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
    737			ref_rate);
    738		return ret;
    739	}
    740
    741	if (!phy_drd->drv_data->has_common_clk_gate) {
    742		phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe");
    743		if (IS_ERR(phy_drd->pipeclk)) {
    744			dev_info(phy_drd->dev,
    745				 "PIPE3 phy operational clock not specified\n");
    746			phy_drd->pipeclk = NULL;
    747		}
    748
    749		phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi");
    750		if (IS_ERR(phy_drd->utmiclk)) {
    751			dev_info(phy_drd->dev,
    752				 "UTMI phy operational clock not specified\n");
    753			phy_drd->utmiclk = NULL;
    754		}
    755
    756		phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp");
    757		if (IS_ERR(phy_drd->itpclk)) {
    758			dev_info(phy_drd->dev,
    759				 "ITP clock from main OSC not specified\n");
    760			phy_drd->itpclk = NULL;
    761		}
    762	}
    763
    764	return 0;
    765}
    766
    767static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
    768	{
    769		.id		= EXYNOS5_DRDPHY_UTMI,
    770		.phy_isol	= exynos5_usbdrd_phy_isol,
    771		.phy_init	= exynos5_usbdrd_utmi_init,
    772		.set_refclk	= exynos5_usbdrd_utmi_set_refclk,
    773	},
    774	{
    775		.id		= EXYNOS5_DRDPHY_PIPE3,
    776		.phy_isol	= exynos5_usbdrd_phy_isol,
    777		.phy_init	= exynos5_usbdrd_pipe3_init,
    778		.set_refclk	= exynos5_usbdrd_pipe3_set_refclk,
    779	},
    780};
    781
    782static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
    783	.phy_cfg		= phy_cfg_exynos5,
    784	.pmu_offset_usbdrd0_phy	= EXYNOS5_USBDRD_PHY_CONTROL,
    785	.pmu_offset_usbdrd1_phy	= EXYNOS5420_USBDRD1_PHY_CONTROL,
    786	.has_common_clk_gate	= true,
    787};
    788
    789static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
    790	.phy_cfg		= phy_cfg_exynos5,
    791	.pmu_offset_usbdrd0_phy	= EXYNOS5_USBDRD_PHY_CONTROL,
    792	.has_common_clk_gate	= true,
    793};
    794
    795static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = {
    796	.phy_cfg		= phy_cfg_exynos5,
    797	.pmu_offset_usbdrd0_phy	= EXYNOS5_USBDRD_PHY_CONTROL,
    798	.pmu_offset_usbdrd1_phy	= EXYNOS5433_USBHOST30_PHY_CONTROL,
    799	.has_common_clk_gate	= false,
    800};
    801
    802static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
    803	.phy_cfg		= phy_cfg_exynos5,
    804	.pmu_offset_usbdrd0_phy	= EXYNOS5_USBDRD_PHY_CONTROL,
    805	.has_common_clk_gate	= false,
    806};
    807
    808static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
    809	{
    810		.compatible = "samsung,exynos5250-usbdrd-phy",
    811		.data = &exynos5250_usbdrd_phy
    812	}, {
    813		.compatible = "samsung,exynos5420-usbdrd-phy",
    814		.data = &exynos5420_usbdrd_phy
    815	}, {
    816		.compatible = "samsung,exynos5433-usbdrd-phy",
    817		.data = &exynos5433_usbdrd_phy
    818	}, {
    819		.compatible = "samsung,exynos7-usbdrd-phy",
    820		.data = &exynos7_usbdrd_phy
    821	},
    822	{ },
    823};
    824MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match);
    825
    826static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
    827{
    828	struct device *dev = &pdev->dev;
    829	struct device_node *node = dev->of_node;
    830	struct exynos5_usbdrd_phy *phy_drd;
    831	struct phy_provider *phy_provider;
    832	const struct exynos5_usbdrd_phy_drvdata *drv_data;
    833	struct regmap *reg_pmu;
    834	u32 pmu_offset;
    835	int i, ret;
    836	int channel;
    837
    838	phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL);
    839	if (!phy_drd)
    840		return -ENOMEM;
    841
    842	dev_set_drvdata(dev, phy_drd);
    843	phy_drd->dev = dev;
    844
    845	phy_drd->reg_phy = devm_platform_ioremap_resource(pdev, 0);
    846	if (IS_ERR(phy_drd->reg_phy))
    847		return PTR_ERR(phy_drd->reg_phy);
    848
    849	drv_data = of_device_get_match_data(dev);
    850	if (!drv_data)
    851		return -EINVAL;
    852
    853	phy_drd->drv_data = drv_data;
    854
    855	ret = exynos5_usbdrd_phy_clk_handle(phy_drd);
    856	if (ret) {
    857		dev_err(dev, "Failed to initialize clocks\n");
    858		return ret;
    859	}
    860
    861	reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
    862						   "samsung,pmu-syscon");
    863	if (IS_ERR(reg_pmu)) {
    864		dev_err(dev, "Failed to lookup PMU regmap\n");
    865		return PTR_ERR(reg_pmu);
    866	}
    867
    868	/*
    869	 * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with
    870	 * each having separate power control registers.
    871	 * 'channel' facilitates to set such registers.
    872	 */
    873	channel = of_alias_get_id(node, "usbdrdphy");
    874	if (channel < 0)
    875		dev_dbg(dev, "Not a multi-controller usbdrd phy\n");
    876
    877	switch (channel) {
    878	case 1:
    879		pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy;
    880		break;
    881	case 0:
    882	default:
    883		pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy;
    884		break;
    885	}
    886
    887	/* Get Vbus regulators */
    888	phy_drd->vbus = devm_regulator_get(dev, "vbus");
    889	if (IS_ERR(phy_drd->vbus)) {
    890		ret = PTR_ERR(phy_drd->vbus);
    891		if (ret == -EPROBE_DEFER)
    892			return ret;
    893
    894		dev_warn(dev, "Failed to get VBUS supply regulator\n");
    895		phy_drd->vbus = NULL;
    896	}
    897
    898	phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost");
    899	if (IS_ERR(phy_drd->vbus_boost)) {
    900		ret = PTR_ERR(phy_drd->vbus_boost);
    901		if (ret == -EPROBE_DEFER)
    902			return ret;
    903
    904		dev_warn(dev, "Failed to get VBUS boost supply regulator\n");
    905		phy_drd->vbus_boost = NULL;
    906	}
    907
    908	dev_vdbg(dev, "Creating usbdrd_phy phy\n");
    909
    910	for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
    911		struct phy *phy = devm_phy_create(dev, NULL,
    912						  &exynos5_usbdrd_phy_ops);
    913		if (IS_ERR(phy)) {
    914			dev_err(dev, "Failed to create usbdrd_phy phy\n");
    915			return PTR_ERR(phy);
    916		}
    917
    918		phy_drd->phys[i].phy = phy;
    919		phy_drd->phys[i].index = i;
    920		phy_drd->phys[i].reg_pmu = reg_pmu;
    921		phy_drd->phys[i].pmu_offset = pmu_offset;
    922		phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i];
    923		phy_set_drvdata(phy, &phy_drd->phys[i]);
    924	}
    925
    926	phy_provider = devm_of_phy_provider_register(dev,
    927						     exynos5_usbdrd_phy_xlate);
    928	if (IS_ERR(phy_provider)) {
    929		dev_err(phy_drd->dev, "Failed to register phy provider\n");
    930		return PTR_ERR(phy_provider);
    931	}
    932
    933	return 0;
    934}
    935
    936static struct platform_driver exynos5_usb3drd_phy = {
    937	.probe	= exynos5_usbdrd_phy_probe,
    938	.driver = {
    939		.of_match_table	= exynos5_usbdrd_phy_of_match,
    940		.name		= "exynos5_usb3drd_phy",
    941		.suppress_bind_attrs = true,
    942	}
    943};
    944
    945module_platform_driver(exynos5_usb3drd_phy);
    946MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver");
    947MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
    948MODULE_LICENSE("GPL v2");
    949MODULE_ALIAS("platform:exynos5_usb3drd_phy");