xref: /openbmc/linux/drivers/phy/qualcomm/phy-qcom-qmp-pcie-msm8996.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
194a407ccSDmitry Baryshkov // SPDX-License-Identifier: GPL-2.0
294a407ccSDmitry Baryshkov /*
394a407ccSDmitry Baryshkov  * Copyright (c) 2017, The Linux Foundation. All rights reserved.
494a407ccSDmitry Baryshkov  */
594a407ccSDmitry Baryshkov 
694a407ccSDmitry Baryshkov #include <linux/clk.h>
794a407ccSDmitry Baryshkov #include <linux/clk-provider.h>
894a407ccSDmitry Baryshkov #include <linux/delay.h>
994a407ccSDmitry Baryshkov #include <linux/err.h>
1094a407ccSDmitry Baryshkov #include <linux/io.h>
1194a407ccSDmitry Baryshkov #include <linux/iopoll.h>
1294a407ccSDmitry Baryshkov #include <linux/kernel.h>
1394a407ccSDmitry Baryshkov #include <linux/module.h>
1494a407ccSDmitry Baryshkov #include <linux/of.h>
1594a407ccSDmitry Baryshkov #include <linux/of_address.h>
1694a407ccSDmitry Baryshkov #include <linux/phy/phy.h>
1794a407ccSDmitry Baryshkov #include <linux/platform_device.h>
1894a407ccSDmitry Baryshkov #include <linux/regulator/consumer.h>
1994a407ccSDmitry Baryshkov #include <linux/reset.h>
2094a407ccSDmitry Baryshkov #include <linux/slab.h>
2194a407ccSDmitry Baryshkov 
2294a407ccSDmitry Baryshkov #include "phy-qcom-qmp.h"
2394a407ccSDmitry Baryshkov 
2494a407ccSDmitry Baryshkov /* QPHY_SW_RESET bit */
2594a407ccSDmitry Baryshkov #define SW_RESET				BIT(0)
2694a407ccSDmitry Baryshkov /* QPHY_POWER_DOWN_CONTROL */
2794a407ccSDmitry Baryshkov #define SW_PWRDN				BIT(0)
2894a407ccSDmitry Baryshkov #define REFCLK_DRV_DSBL				BIT(1)
2994a407ccSDmitry Baryshkov /* QPHY_START_CONTROL bits */
3094a407ccSDmitry Baryshkov #define SERDES_START				BIT(0)
3194a407ccSDmitry Baryshkov #define PCS_START				BIT(1)
3294a407ccSDmitry Baryshkov #define PLL_READY_GATE_EN			BIT(3)
3394a407ccSDmitry Baryshkov /* QPHY_PCS_STATUS bit */
3494a407ccSDmitry Baryshkov #define PHYSTATUS				BIT(6)
357936e5f3SJohan Hovold /* QPHY_COM_PCS_READY_STATUS bit */
3694a407ccSDmitry Baryshkov #define PCS_READY				BIT(0)
3794a407ccSDmitry Baryshkov 
3894a407ccSDmitry Baryshkov #define PHY_INIT_COMPLETE_TIMEOUT		10000
3994a407ccSDmitry Baryshkov #define POWER_DOWN_DELAY_US_MIN			10
40abc08416SJohan Hovold #define POWER_DOWN_DELAY_US_MAX			20
4194a407ccSDmitry Baryshkov 
4294a407ccSDmitry Baryshkov struct qmp_phy_init_tbl {
4394a407ccSDmitry Baryshkov 	unsigned int offset;
4494a407ccSDmitry Baryshkov 	unsigned int val;
4594a407ccSDmitry Baryshkov 	/*
4694a407ccSDmitry Baryshkov 	 * mask of lanes for which this register is written
4794a407ccSDmitry Baryshkov 	 * for cases when second lane needs different values
4894a407ccSDmitry Baryshkov 	 */
4994a407ccSDmitry Baryshkov 	u8 lane_mask;
5094a407ccSDmitry Baryshkov };
5194a407ccSDmitry Baryshkov 
5294a407ccSDmitry Baryshkov #define QMP_PHY_INIT_CFG(o, v)		\
5394a407ccSDmitry Baryshkov 	{				\
5494a407ccSDmitry Baryshkov 		.offset = o,		\
5594a407ccSDmitry Baryshkov 		.val = v,		\
5694a407ccSDmitry Baryshkov 		.lane_mask = 0xff,	\
5794a407ccSDmitry Baryshkov 	}
5894a407ccSDmitry Baryshkov 
5994a407ccSDmitry Baryshkov #define QMP_PHY_INIT_CFG_LANE(o, v, l)	\
6094a407ccSDmitry Baryshkov 	{				\
6194a407ccSDmitry Baryshkov 		.offset = o,		\
6294a407ccSDmitry Baryshkov 		.val = v,		\
6394a407ccSDmitry Baryshkov 		.lane_mask = l,		\
6494a407ccSDmitry Baryshkov 	}
6594a407ccSDmitry Baryshkov 
6694a407ccSDmitry Baryshkov /* set of registers with offsets different per-PHY */
6794a407ccSDmitry Baryshkov enum qphy_reg_layout {
6894a407ccSDmitry Baryshkov 	/* Common block control registers */
6994a407ccSDmitry Baryshkov 	QPHY_COM_SW_RESET,
7094a407ccSDmitry Baryshkov 	QPHY_COM_POWER_DOWN_CONTROL,
7194a407ccSDmitry Baryshkov 	QPHY_COM_START_CONTROL,
7294a407ccSDmitry Baryshkov 	QPHY_COM_PCS_READY_STATUS,
7394a407ccSDmitry Baryshkov 	/* PCS registers */
7494a407ccSDmitry Baryshkov 	QPHY_SW_RESET,
7594a407ccSDmitry Baryshkov 	QPHY_START_CTRL,
7694a407ccSDmitry Baryshkov 	QPHY_PCS_STATUS,
7794a407ccSDmitry Baryshkov 	/* Keep last to ensure regs_layout arrays are properly initialized */
7894a407ccSDmitry Baryshkov 	QPHY_LAYOUT_SIZE
7994a407ccSDmitry Baryshkov };
8094a407ccSDmitry Baryshkov 
8194a407ccSDmitry Baryshkov static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
8294a407ccSDmitry Baryshkov 	[QPHY_COM_SW_RESET]		= 0x400,
8394a407ccSDmitry Baryshkov 	[QPHY_COM_POWER_DOWN_CONTROL]	= 0x404,
8494a407ccSDmitry Baryshkov 	[QPHY_COM_START_CONTROL]	= 0x408,
8594a407ccSDmitry Baryshkov 	[QPHY_COM_PCS_READY_STATUS]	= 0x448,
8661f21e0eSDmitry Baryshkov 	[QPHY_SW_RESET]			= QPHY_V2_PCS_SW_RESET,
8761f21e0eSDmitry Baryshkov 	[QPHY_START_CTRL]		= QPHY_V2_PCS_START_CONTROL,
8861f21e0eSDmitry Baryshkov 	[QPHY_PCS_STATUS]		= QPHY_V2_PCS_PCI_PCS_STATUS,
8994a407ccSDmitry Baryshkov };
9094a407ccSDmitry Baryshkov 
9194a407ccSDmitry Baryshkov static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
9294a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c),
9394a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
9494a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
9594a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06),
9694a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42),
9794a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00),
9894a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
9994a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f),
10094a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01),
10194a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01),
10294a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
10394a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
10494a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09),
10594a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
10694a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03),
10794a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55),
10894a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55),
10994a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
11094a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a),
11194a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a),
11294a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33),
11394a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02),
11494a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f),
11594a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04),
11694a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
11794a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
11894a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
11994a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
12094a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
12194a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01),
12294a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31),
12394a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01),
12494a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02),
12594a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00),
12694a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f),
12794a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19),
12894a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15),
12994a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f),
13094a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f),
13194a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19),
13294a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10),
13394a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00),
13494a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40),
13594a407ccSDmitry Baryshkov };
13694a407ccSDmitry Baryshkov 
13794a407ccSDmitry Baryshkov static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = {
13894a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
13994a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06),
14094a407ccSDmitry Baryshkov };
14194a407ccSDmitry Baryshkov 
14294a407ccSDmitry Baryshkov static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = {
14394a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c),
14494a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01),
14594a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00),
14694a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb),
14794a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18),
14894a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04),
14994a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04),
15094a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
15194a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
15294a407ccSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19),
15394a407ccSDmitry Baryshkov };
15494a407ccSDmitry Baryshkov 
15594a407ccSDmitry Baryshkov static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = {
1566cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_IDLE_DTCT_CNTRL, 0x4c),
1576cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00),
1586cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
15994a407ccSDmitry Baryshkov 
160c1ab64aaSDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME, 0x05),
16194a407ccSDmitry Baryshkov 
1626cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE, 0x05),
1636cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_DOWN_CONTROL, 0x02),
1646cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG4, 0x00),
1656cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG1, 0xa3),
1666cad2983SDmitry Baryshkov 	QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0, 0x0e),
16794a407ccSDmitry Baryshkov };
16894a407ccSDmitry Baryshkov 
16994a407ccSDmitry Baryshkov /* struct qmp_phy_cfg - per-PHY initialization config */
17094a407ccSDmitry Baryshkov struct qmp_phy_cfg {
1710d316ce5SJohan Hovold 	/* number of PHYs provided by this block */
1720d316ce5SJohan Hovold 	int num_phys;
17394a407ccSDmitry Baryshkov 
17494a407ccSDmitry Baryshkov 	/* Init sequence for PHY blocks - serdes, tx, rx, pcs */
17594a407ccSDmitry Baryshkov 	const struct qmp_phy_init_tbl *serdes_tbl;
17694a407ccSDmitry Baryshkov 	int serdes_tbl_num;
17794a407ccSDmitry Baryshkov 	const struct qmp_phy_init_tbl *tx_tbl;
17894a407ccSDmitry Baryshkov 	int tx_tbl_num;
17994a407ccSDmitry Baryshkov 	const struct qmp_phy_init_tbl *rx_tbl;
18094a407ccSDmitry Baryshkov 	int rx_tbl_num;
18194a407ccSDmitry Baryshkov 	const struct qmp_phy_init_tbl *pcs_tbl;
18294a407ccSDmitry Baryshkov 	int pcs_tbl_num;
18394a407ccSDmitry Baryshkov 
18494a407ccSDmitry Baryshkov 	/* clock ids to be requested */
18594a407ccSDmitry Baryshkov 	const char * const *clk_list;
18694a407ccSDmitry Baryshkov 	int num_clks;
18794a407ccSDmitry Baryshkov 	/* resets to be requested */
18894a407ccSDmitry Baryshkov 	const char * const *reset_list;
18994a407ccSDmitry Baryshkov 	int num_resets;
19094a407ccSDmitry Baryshkov 	/* regulators to be requested */
19194a407ccSDmitry Baryshkov 	const char * const *vreg_list;
19294a407ccSDmitry Baryshkov 	int num_vregs;
19394a407ccSDmitry Baryshkov 
19494a407ccSDmitry Baryshkov 	/* array of registers with different offsets */
19594a407ccSDmitry Baryshkov 	const unsigned int *regs;
19694a407ccSDmitry Baryshkov };
19794a407ccSDmitry Baryshkov 
19894a407ccSDmitry Baryshkov /**
19994a407ccSDmitry Baryshkov  * struct qmp_phy - per-lane phy descriptor
20094a407ccSDmitry Baryshkov  *
20194a407ccSDmitry Baryshkov  * @phy: generic phy
20294a407ccSDmitry Baryshkov  * @cfg: phy specific configuration
20394a407ccSDmitry Baryshkov  * @serdes: iomapped memory space for phy's serdes (i.e. PLL)
20494a407ccSDmitry Baryshkov  * @tx: iomapped memory space for lane's tx
20594a407ccSDmitry Baryshkov  * @rx: iomapped memory space for lane's rx
20694a407ccSDmitry Baryshkov  * @pcs: iomapped memory space for lane's pcs
20794a407ccSDmitry Baryshkov  * @pipe_clk: pipe clock
20894a407ccSDmitry Baryshkov  * @index: lane index
20994a407ccSDmitry Baryshkov  * @qmp: QMP phy to which this lane belongs
21094a407ccSDmitry Baryshkov  * @lane_rst: lane's reset controller
21194a407ccSDmitry Baryshkov  */
21294a407ccSDmitry Baryshkov struct qmp_phy {
21394a407ccSDmitry Baryshkov 	struct phy *phy;
21494a407ccSDmitry Baryshkov 	const struct qmp_phy_cfg *cfg;
21594a407ccSDmitry Baryshkov 	void __iomem *serdes;
21694a407ccSDmitry Baryshkov 	void __iomem *tx;
21794a407ccSDmitry Baryshkov 	void __iomem *rx;
21894a407ccSDmitry Baryshkov 	void __iomem *pcs;
21994a407ccSDmitry Baryshkov 	struct clk *pipe_clk;
22094a407ccSDmitry Baryshkov 	unsigned int index;
22194a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp;
22294a407ccSDmitry Baryshkov 	struct reset_control *lane_rst;
22394a407ccSDmitry Baryshkov };
22494a407ccSDmitry Baryshkov 
22594a407ccSDmitry Baryshkov /**
22694a407ccSDmitry Baryshkov  * struct qcom_qmp - structure holding QMP phy block attributes
22794a407ccSDmitry Baryshkov  *
22894a407ccSDmitry Baryshkov  * @dev: device
22994a407ccSDmitry Baryshkov  *
23094a407ccSDmitry Baryshkov  * @clks: array of clocks required by phy
23194a407ccSDmitry Baryshkov  * @resets: array of resets required by phy
23294a407ccSDmitry Baryshkov  * @vregs: regulator supplies bulk data
23394a407ccSDmitry Baryshkov  *
23494a407ccSDmitry Baryshkov  * @phys: array of per-lane phy descriptors
23594a407ccSDmitry Baryshkov  * @phy_mutex: mutex lock for PHY common block initialization
23694a407ccSDmitry Baryshkov  * @init_count: phy common block initialization count
23794a407ccSDmitry Baryshkov  */
23894a407ccSDmitry Baryshkov struct qcom_qmp {
23994a407ccSDmitry Baryshkov 	struct device *dev;
24094a407ccSDmitry Baryshkov 
24194a407ccSDmitry Baryshkov 	struct clk_bulk_data *clks;
242ccac0847SDmitry Baryshkov 	struct reset_control_bulk_data *resets;
24394a407ccSDmitry Baryshkov 	struct regulator_bulk_data *vregs;
24494a407ccSDmitry Baryshkov 
24594a407ccSDmitry Baryshkov 	struct qmp_phy **phys;
24694a407ccSDmitry Baryshkov 
24794a407ccSDmitry Baryshkov 	struct mutex phy_mutex;
24894a407ccSDmitry Baryshkov 	int init_count;
24994a407ccSDmitry Baryshkov };
25094a407ccSDmitry Baryshkov 
qphy_setbits(void __iomem * base,u32 offset,u32 val)25194a407ccSDmitry Baryshkov static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
25294a407ccSDmitry Baryshkov {
25394a407ccSDmitry Baryshkov 	u32 reg;
25494a407ccSDmitry Baryshkov 
25594a407ccSDmitry Baryshkov 	reg = readl(base + offset);
25694a407ccSDmitry Baryshkov 	reg |= val;
25794a407ccSDmitry Baryshkov 	writel(reg, base + offset);
25894a407ccSDmitry Baryshkov 
25994a407ccSDmitry Baryshkov 	/* ensure that above write is through */
26094a407ccSDmitry Baryshkov 	readl(base + offset);
26194a407ccSDmitry Baryshkov }
26294a407ccSDmitry Baryshkov 
qphy_clrbits(void __iomem * base,u32 offset,u32 val)26394a407ccSDmitry Baryshkov static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val)
26494a407ccSDmitry Baryshkov {
26594a407ccSDmitry Baryshkov 	u32 reg;
26694a407ccSDmitry Baryshkov 
26794a407ccSDmitry Baryshkov 	reg = readl(base + offset);
26894a407ccSDmitry Baryshkov 	reg &= ~val;
26994a407ccSDmitry Baryshkov 	writel(reg, base + offset);
27094a407ccSDmitry Baryshkov 
27194a407ccSDmitry Baryshkov 	/* ensure that above write is through */
27294a407ccSDmitry Baryshkov 	readl(base + offset);
27394a407ccSDmitry Baryshkov }
27494a407ccSDmitry Baryshkov 
27594a407ccSDmitry Baryshkov /* list of clocks required by phy */
27694a407ccSDmitry Baryshkov static const char * const msm8996_phy_clk_l[] = {
27794a407ccSDmitry Baryshkov 	"aux", "cfg_ahb", "ref",
27894a407ccSDmitry Baryshkov };
27994a407ccSDmitry Baryshkov 
28094a407ccSDmitry Baryshkov /* list of resets */
28194a407ccSDmitry Baryshkov static const char * const msm8996_pciephy_reset_l[] = {
28294a407ccSDmitry Baryshkov 	"phy", "common", "cfg",
28394a407ccSDmitry Baryshkov };
28494a407ccSDmitry Baryshkov 
28594a407ccSDmitry Baryshkov /* list of regulators */
28694a407ccSDmitry Baryshkov static const char * const qmp_phy_vreg_l[] = {
28794a407ccSDmitry Baryshkov 	"vdda-phy", "vdda-pll",
28894a407ccSDmitry Baryshkov };
28994a407ccSDmitry Baryshkov 
29094a407ccSDmitry Baryshkov static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
2910d316ce5SJohan Hovold 	.num_phys		= 3,
29294a407ccSDmitry Baryshkov 
29394a407ccSDmitry Baryshkov 	.serdes_tbl		= msm8996_pcie_serdes_tbl,
29494a407ccSDmitry Baryshkov 	.serdes_tbl_num		= ARRAY_SIZE(msm8996_pcie_serdes_tbl),
29594a407ccSDmitry Baryshkov 	.tx_tbl			= msm8996_pcie_tx_tbl,
29694a407ccSDmitry Baryshkov 	.tx_tbl_num		= ARRAY_SIZE(msm8996_pcie_tx_tbl),
29794a407ccSDmitry Baryshkov 	.rx_tbl			= msm8996_pcie_rx_tbl,
29894a407ccSDmitry Baryshkov 	.rx_tbl_num		= ARRAY_SIZE(msm8996_pcie_rx_tbl),
29994a407ccSDmitry Baryshkov 	.pcs_tbl		= msm8996_pcie_pcs_tbl,
30094a407ccSDmitry Baryshkov 	.pcs_tbl_num		= ARRAY_SIZE(msm8996_pcie_pcs_tbl),
30194a407ccSDmitry Baryshkov 	.clk_list		= msm8996_phy_clk_l,
30294a407ccSDmitry Baryshkov 	.num_clks		= ARRAY_SIZE(msm8996_phy_clk_l),
30394a407ccSDmitry Baryshkov 	.reset_list		= msm8996_pciephy_reset_l,
30494a407ccSDmitry Baryshkov 	.num_resets		= ARRAY_SIZE(msm8996_pciephy_reset_l),
30594a407ccSDmitry Baryshkov 	.vreg_list		= qmp_phy_vreg_l,
30694a407ccSDmitry Baryshkov 	.num_vregs		= ARRAY_SIZE(qmp_phy_vreg_l),
30794a407ccSDmitry Baryshkov 	.regs			= pciephy_regs_layout,
30894a407ccSDmitry Baryshkov };
30994a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_configure_lane(void __iomem * base,const struct qmp_phy_init_tbl tbl[],int num,u8 lane_mask)310c577468cSJohan Hovold static void qmp_pcie_msm8996_configure_lane(void __iomem *base,
31194a407ccSDmitry Baryshkov 					const struct qmp_phy_init_tbl tbl[],
31294a407ccSDmitry Baryshkov 					int num,
31394a407ccSDmitry Baryshkov 					u8 lane_mask)
31494a407ccSDmitry Baryshkov {
31594a407ccSDmitry Baryshkov 	int i;
31694a407ccSDmitry Baryshkov 	const struct qmp_phy_init_tbl *t = tbl;
31794a407ccSDmitry Baryshkov 
31894a407ccSDmitry Baryshkov 	if (!t)
31994a407ccSDmitry Baryshkov 		return;
32094a407ccSDmitry Baryshkov 
32194a407ccSDmitry Baryshkov 	for (i = 0; i < num; i++, t++) {
32294a407ccSDmitry Baryshkov 		if (!(t->lane_mask & lane_mask))
32394a407ccSDmitry Baryshkov 			continue;
32494a407ccSDmitry Baryshkov 
32594a407ccSDmitry Baryshkov 		writel(t->val, base + t->offset);
32694a407ccSDmitry Baryshkov 	}
32794a407ccSDmitry Baryshkov }
32894a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_configure(void __iomem * base,const struct qmp_phy_init_tbl tbl[],int num)329c577468cSJohan Hovold static void qmp_pcie_msm8996_configure(void __iomem *base,
33094a407ccSDmitry Baryshkov 				   const struct qmp_phy_init_tbl tbl[],
33194a407ccSDmitry Baryshkov 				   int num)
33294a407ccSDmitry Baryshkov {
333d3ef8863SJohan Hovold 	qmp_pcie_msm8996_configure_lane(base, tbl, num, 0xff);
33494a407ccSDmitry Baryshkov }
33594a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_serdes_init(struct qmp_phy * qphy)336c577468cSJohan Hovold static int qmp_pcie_msm8996_serdes_init(struct qmp_phy *qphy)
33794a407ccSDmitry Baryshkov {
33894a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = qphy->qmp;
33994a407ccSDmitry Baryshkov 	const struct qmp_phy_cfg *cfg = qphy->cfg;
34094a407ccSDmitry Baryshkov 	void __iomem *serdes = qphy->serdes;
34194a407ccSDmitry Baryshkov 	const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl;
34294a407ccSDmitry Baryshkov 	int serdes_tbl_num = cfg->serdes_tbl_num;
3434856865bSDmitry Baryshkov 	void __iomem *status;
3441a3ae97cSJohan Hovold 	unsigned int val;
34594a407ccSDmitry Baryshkov 	int ret;
34694a407ccSDmitry Baryshkov 
347d3ef8863SJohan Hovold 	qmp_pcie_msm8996_configure(serdes, serdes_tbl, serdes_tbl_num);
34894a407ccSDmitry Baryshkov 
34994a407ccSDmitry Baryshkov 	qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET);
35094a407ccSDmitry Baryshkov 	qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
35194a407ccSDmitry Baryshkov 		     SERDES_START | PCS_START);
35294a407ccSDmitry Baryshkov 
35394a407ccSDmitry Baryshkov 	status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS];
3543894f6d0SJohan Hovold 	ret = readl_poll_timeout(status, val, (val & PCS_READY), 200,
35594a407ccSDmitry Baryshkov 				 PHY_INIT_COMPLETE_TIMEOUT);
35694a407ccSDmitry Baryshkov 	if (ret) {
35794a407ccSDmitry Baryshkov 		dev_err(qmp->dev,
35894a407ccSDmitry Baryshkov 			"phy common block init timed-out\n");
35994a407ccSDmitry Baryshkov 		return ret;
36094a407ccSDmitry Baryshkov 	}
36194a407ccSDmitry Baryshkov 
36294a407ccSDmitry Baryshkov 	return 0;
36394a407ccSDmitry Baryshkov }
36494a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_com_init(struct qmp_phy * qphy)365c577468cSJohan Hovold static int qmp_pcie_msm8996_com_init(struct qmp_phy *qphy)
36694a407ccSDmitry Baryshkov {
36794a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = qphy->qmp;
36894a407ccSDmitry Baryshkov 	const struct qmp_phy_cfg *cfg = qphy->cfg;
36994a407ccSDmitry Baryshkov 	void __iomem *serdes = qphy->serdes;
370ccac0847SDmitry Baryshkov 	int ret;
37194a407ccSDmitry Baryshkov 
37294a407ccSDmitry Baryshkov 	mutex_lock(&qmp->phy_mutex);
37394a407ccSDmitry Baryshkov 	if (qmp->init_count++) {
37494a407ccSDmitry Baryshkov 		mutex_unlock(&qmp->phy_mutex);
37594a407ccSDmitry Baryshkov 		return 0;
37694a407ccSDmitry Baryshkov 	}
37794a407ccSDmitry Baryshkov 
37894a407ccSDmitry Baryshkov 	ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
37994a407ccSDmitry Baryshkov 	if (ret) {
38094a407ccSDmitry Baryshkov 		dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
381*e42f1107SJohan Hovold 		goto err_decrement_count;
38294a407ccSDmitry Baryshkov 	}
38394a407ccSDmitry Baryshkov 
384ccac0847SDmitry Baryshkov 	ret = reset_control_bulk_assert(cfg->num_resets, qmp->resets);
38594a407ccSDmitry Baryshkov 	if (ret) {
386ccac0847SDmitry Baryshkov 		dev_err(qmp->dev, "reset assert failed\n");
38794a407ccSDmitry Baryshkov 		goto err_disable_regulators;
38894a407ccSDmitry Baryshkov 	}
38994a407ccSDmitry Baryshkov 
390ccac0847SDmitry Baryshkov 	ret = reset_control_bulk_deassert(cfg->num_resets, qmp->resets);
39194a407ccSDmitry Baryshkov 	if (ret) {
392ccac0847SDmitry Baryshkov 		dev_err(qmp->dev, "reset deassert failed\n");
393ccac0847SDmitry Baryshkov 		goto err_disable_regulators;
39494a407ccSDmitry Baryshkov 	}
39594a407ccSDmitry Baryshkov 
39694a407ccSDmitry Baryshkov 	ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks);
39794a407ccSDmitry Baryshkov 	if (ret)
39894a407ccSDmitry Baryshkov 		goto err_assert_reset;
39994a407ccSDmitry Baryshkov 
40094a407ccSDmitry Baryshkov 	qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
40194a407ccSDmitry Baryshkov 		     SW_PWRDN);
40294a407ccSDmitry Baryshkov 
40394a407ccSDmitry Baryshkov 	mutex_unlock(&qmp->phy_mutex);
40494a407ccSDmitry Baryshkov 
40594a407ccSDmitry Baryshkov 	return 0;
40694a407ccSDmitry Baryshkov 
40794a407ccSDmitry Baryshkov err_assert_reset:
408ccac0847SDmitry Baryshkov 	reset_control_bulk_assert(cfg->num_resets, qmp->resets);
40994a407ccSDmitry Baryshkov err_disable_regulators:
41094a407ccSDmitry Baryshkov 	regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
411*e42f1107SJohan Hovold err_decrement_count:
412*e42f1107SJohan Hovold 	qmp->init_count--;
41394a407ccSDmitry Baryshkov 	mutex_unlock(&qmp->phy_mutex);
41494a407ccSDmitry Baryshkov 
41594a407ccSDmitry Baryshkov 	return ret;
41694a407ccSDmitry Baryshkov }
41794a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_com_exit(struct qmp_phy * qphy)418c577468cSJohan Hovold static int qmp_pcie_msm8996_com_exit(struct qmp_phy *qphy)
41994a407ccSDmitry Baryshkov {
42094a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = qphy->qmp;
42194a407ccSDmitry Baryshkov 	const struct qmp_phy_cfg *cfg = qphy->cfg;
42294a407ccSDmitry Baryshkov 	void __iomem *serdes = qphy->serdes;
42394a407ccSDmitry Baryshkov 
42494a407ccSDmitry Baryshkov 	mutex_lock(&qmp->phy_mutex);
42594a407ccSDmitry Baryshkov 	if (--qmp->init_count) {
42694a407ccSDmitry Baryshkov 		mutex_unlock(&qmp->phy_mutex);
42794a407ccSDmitry Baryshkov 		return 0;
42894a407ccSDmitry Baryshkov 	}
42994a407ccSDmitry Baryshkov 
43094a407ccSDmitry Baryshkov 	qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
43194a407ccSDmitry Baryshkov 		     SERDES_START | PCS_START);
43294a407ccSDmitry Baryshkov 	qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET],
43394a407ccSDmitry Baryshkov 		     SW_RESET);
43494a407ccSDmitry Baryshkov 	qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
43594a407ccSDmitry Baryshkov 		     SW_PWRDN);
43694a407ccSDmitry Baryshkov 
437ccac0847SDmitry Baryshkov 	reset_control_bulk_assert(cfg->num_resets, qmp->resets);
43894a407ccSDmitry Baryshkov 
43994a407ccSDmitry Baryshkov 	clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks);
44094a407ccSDmitry Baryshkov 
44194a407ccSDmitry Baryshkov 	regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
44294a407ccSDmitry Baryshkov 
44394a407ccSDmitry Baryshkov 	mutex_unlock(&qmp->phy_mutex);
44494a407ccSDmitry Baryshkov 
44594a407ccSDmitry Baryshkov 	return 0;
44694a407ccSDmitry Baryshkov }
44794a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_init(struct phy * phy)448c577468cSJohan Hovold static int qmp_pcie_msm8996_init(struct phy *phy)
44994a407ccSDmitry Baryshkov {
45094a407ccSDmitry Baryshkov 	struct qmp_phy *qphy = phy_get_drvdata(phy);
45194a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = qphy->qmp;
45294a407ccSDmitry Baryshkov 	int ret;
45394a407ccSDmitry Baryshkov 	dev_vdbg(qmp->dev, "Initializing QMP phy\n");
45494a407ccSDmitry Baryshkov 
455c577468cSJohan Hovold 	ret = qmp_pcie_msm8996_com_init(qphy);
45694a407ccSDmitry Baryshkov 	if (ret)
45794a407ccSDmitry Baryshkov 		return ret;
45894a407ccSDmitry Baryshkov 
45994a407ccSDmitry Baryshkov 	return 0;
46094a407ccSDmitry Baryshkov }
46194a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_power_on(struct phy * phy)462c577468cSJohan Hovold static int qmp_pcie_msm8996_power_on(struct phy *phy)
46394a407ccSDmitry Baryshkov {
46494a407ccSDmitry Baryshkov 	struct qmp_phy *qphy = phy_get_drvdata(phy);
46594a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = qphy->qmp;
46694a407ccSDmitry Baryshkov 	const struct qmp_phy_cfg *cfg = qphy->cfg;
46794a407ccSDmitry Baryshkov 	void __iomem *tx = qphy->tx;
46894a407ccSDmitry Baryshkov 	void __iomem *rx = qphy->rx;
46994a407ccSDmitry Baryshkov 	void __iomem *pcs = qphy->pcs;
47094a407ccSDmitry Baryshkov 	void __iomem *status;
4711a3ae97cSJohan Hovold 	unsigned int val;
47294a407ccSDmitry Baryshkov 	int ret;
47394a407ccSDmitry Baryshkov 
474c577468cSJohan Hovold 	qmp_pcie_msm8996_serdes_init(qphy);
47594a407ccSDmitry Baryshkov 
47694a407ccSDmitry Baryshkov 	ret = reset_control_deassert(qphy->lane_rst);
47794a407ccSDmitry Baryshkov 	if (ret) {
47894a407ccSDmitry Baryshkov 		dev_err(qmp->dev, "lane%d reset deassert failed\n",
47994a407ccSDmitry Baryshkov 			qphy->index);
48094a407ccSDmitry Baryshkov 		return ret;
48194a407ccSDmitry Baryshkov 	}
48294a407ccSDmitry Baryshkov 
48394a407ccSDmitry Baryshkov 	ret = clk_prepare_enable(qphy->pipe_clk);
48494a407ccSDmitry Baryshkov 	if (ret) {
48594a407ccSDmitry Baryshkov 		dev_err(qmp->dev, "pipe_clk enable failed err=%d\n", ret);
48694a407ccSDmitry Baryshkov 		goto err_reset_lane;
48794a407ccSDmitry Baryshkov 	}
48894a407ccSDmitry Baryshkov 
48994a407ccSDmitry Baryshkov 	/* Tx, Rx, and PCS configurations */
490d3ef8863SJohan Hovold 	qmp_pcie_msm8996_configure_lane(tx, cfg->tx_tbl, cfg->tx_tbl_num, 1);
491d3ef8863SJohan Hovold 	qmp_pcie_msm8996_configure_lane(rx, cfg->rx_tbl, cfg->rx_tbl_num, 1);
492d3ef8863SJohan Hovold 	qmp_pcie_msm8996_configure(pcs, cfg->pcs_tbl, cfg->pcs_tbl_num);
49394a407ccSDmitry Baryshkov 
49494a407ccSDmitry Baryshkov 	/*
49594a407ccSDmitry Baryshkov 	 * Pull out PHY from POWER DOWN state.
49694a407ccSDmitry Baryshkov 	 * This is active low enable signal to power-down PHY.
49794a407ccSDmitry Baryshkov 	 */
4983d3db6f0SJohan Hovold 	qphy_setbits(pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
4993d3db6f0SJohan Hovold 			SW_PWRDN | REFCLK_DRV_DSBL);
50094a407ccSDmitry Baryshkov 
501abc08416SJohan Hovold 	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
50294a407ccSDmitry Baryshkov 
50394a407ccSDmitry Baryshkov 	/* Pull PHY out of reset state */
50494a407ccSDmitry Baryshkov 	qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
5054856865bSDmitry Baryshkov 
50694a407ccSDmitry Baryshkov 	/* start SerDes and Phy-Coding-Sublayer */
5073d3db6f0SJohan Hovold 	qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL],
5083d3db6f0SJohan Hovold 			PCS_START | PLL_READY_GATE_EN);
50994a407ccSDmitry Baryshkov 
51094a407ccSDmitry Baryshkov 	status = pcs + cfg->regs[QPHY_PCS_STATUS];
5113894f6d0SJohan Hovold 	ret = readl_poll_timeout(status, val, !(val & PHYSTATUS), 200,
51294a407ccSDmitry Baryshkov 				 PHY_INIT_COMPLETE_TIMEOUT);
51394a407ccSDmitry Baryshkov 	if (ret) {
51494a407ccSDmitry Baryshkov 		dev_err(qmp->dev, "phy initialization timed-out\n");
51594a407ccSDmitry Baryshkov 		goto err_disable_pipe_clk;
51694a407ccSDmitry Baryshkov 	}
517f575ac2dSDmitry Baryshkov 
51894a407ccSDmitry Baryshkov 	return 0;
51994a407ccSDmitry Baryshkov 
52094a407ccSDmitry Baryshkov err_disable_pipe_clk:
52194a407ccSDmitry Baryshkov 	clk_disable_unprepare(qphy->pipe_clk);
52294a407ccSDmitry Baryshkov err_reset_lane:
52394a407ccSDmitry Baryshkov 	reset_control_assert(qphy->lane_rst);
52494a407ccSDmitry Baryshkov 
52594a407ccSDmitry Baryshkov 	return ret;
52694a407ccSDmitry Baryshkov }
52794a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_power_off(struct phy * phy)528c577468cSJohan Hovold static int qmp_pcie_msm8996_power_off(struct phy *phy)
52994a407ccSDmitry Baryshkov {
53094a407ccSDmitry Baryshkov 	struct qmp_phy *qphy = phy_get_drvdata(phy);
53194a407ccSDmitry Baryshkov 	const struct qmp_phy_cfg *cfg = qphy->cfg;
53294a407ccSDmitry Baryshkov 
53394a407ccSDmitry Baryshkov 	clk_disable_unprepare(qphy->pipe_clk);
53494a407ccSDmitry Baryshkov 
53594a407ccSDmitry Baryshkov 	/* PHY reset */
53694a407ccSDmitry Baryshkov 	qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
53794a407ccSDmitry Baryshkov 
53894a407ccSDmitry Baryshkov 	/* stop SerDes and Phy-Coding-Sublayer */
5393d3db6f0SJohan Hovold 	qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL],
5403d3db6f0SJohan Hovold 			SERDES_START | PCS_START);
54194a407ccSDmitry Baryshkov 
54294a407ccSDmitry Baryshkov 	/* Put PHY into POWER DOWN state: active low */
5436cad2983SDmitry Baryshkov 	qphy_clrbits(qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL,
5443d3db6f0SJohan Hovold 			SW_PWRDN | REFCLK_DRV_DSBL);
54594a407ccSDmitry Baryshkov 
54694a407ccSDmitry Baryshkov 	return 0;
54794a407ccSDmitry Baryshkov }
54894a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_exit(struct phy * phy)549c577468cSJohan Hovold static int qmp_pcie_msm8996_exit(struct phy *phy)
55094a407ccSDmitry Baryshkov {
55194a407ccSDmitry Baryshkov 	struct qmp_phy *qphy = phy_get_drvdata(phy);
55294a407ccSDmitry Baryshkov 
55394a407ccSDmitry Baryshkov 	reset_control_assert(qphy->lane_rst);
55494a407ccSDmitry Baryshkov 
555c577468cSJohan Hovold 	qmp_pcie_msm8996_com_exit(qphy);
55694a407ccSDmitry Baryshkov 
55794a407ccSDmitry Baryshkov 	return 0;
55894a407ccSDmitry Baryshkov }
55994a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_enable(struct phy * phy)560c577468cSJohan Hovold static int qmp_pcie_msm8996_enable(struct phy *phy)
56194a407ccSDmitry Baryshkov {
56294a407ccSDmitry Baryshkov 	int ret;
56394a407ccSDmitry Baryshkov 
564c577468cSJohan Hovold 	ret = qmp_pcie_msm8996_init(phy);
56594a407ccSDmitry Baryshkov 	if (ret)
56694a407ccSDmitry Baryshkov 		return ret;
56794a407ccSDmitry Baryshkov 
568c577468cSJohan Hovold 	ret = qmp_pcie_msm8996_power_on(phy);
56994a407ccSDmitry Baryshkov 	if (ret)
570c577468cSJohan Hovold 		qmp_pcie_msm8996_exit(phy);
57194a407ccSDmitry Baryshkov 
57294a407ccSDmitry Baryshkov 	return ret;
57394a407ccSDmitry Baryshkov }
57494a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_disable(struct phy * phy)575c577468cSJohan Hovold static int qmp_pcie_msm8996_disable(struct phy *phy)
57694a407ccSDmitry Baryshkov {
57794a407ccSDmitry Baryshkov 	int ret;
57894a407ccSDmitry Baryshkov 
579c577468cSJohan Hovold 	ret = qmp_pcie_msm8996_power_off(phy);
58094a407ccSDmitry Baryshkov 	if (ret)
58194a407ccSDmitry Baryshkov 		return ret;
582c577468cSJohan Hovold 	return qmp_pcie_msm8996_exit(phy);
58394a407ccSDmitry Baryshkov }
58494a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_vreg_init(struct device * dev,const struct qmp_phy_cfg * cfg)585c577468cSJohan Hovold static int qmp_pcie_msm8996_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg)
58694a407ccSDmitry Baryshkov {
58794a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = dev_get_drvdata(dev);
58894a407ccSDmitry Baryshkov 	int num = cfg->num_vregs;
58994a407ccSDmitry Baryshkov 	int i;
59094a407ccSDmitry Baryshkov 
59194a407ccSDmitry Baryshkov 	qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL);
59294a407ccSDmitry Baryshkov 	if (!qmp->vregs)
59394a407ccSDmitry Baryshkov 		return -ENOMEM;
59494a407ccSDmitry Baryshkov 
59594a407ccSDmitry Baryshkov 	for (i = 0; i < num; i++)
59694a407ccSDmitry Baryshkov 		qmp->vregs[i].supply = cfg->vreg_list[i];
59794a407ccSDmitry Baryshkov 
59894a407ccSDmitry Baryshkov 	return devm_regulator_bulk_get(dev, num, qmp->vregs);
59994a407ccSDmitry Baryshkov }
60094a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_reset_init(struct device * dev,const struct qmp_phy_cfg * cfg)601c577468cSJohan Hovold static int qmp_pcie_msm8996_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg)
60294a407ccSDmitry Baryshkov {
60394a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = dev_get_drvdata(dev);
60494a407ccSDmitry Baryshkov 	int i;
605ccac0847SDmitry Baryshkov 	int ret;
60694a407ccSDmitry Baryshkov 
60794a407ccSDmitry Baryshkov 	qmp->resets = devm_kcalloc(dev, cfg->num_resets,
60894a407ccSDmitry Baryshkov 				   sizeof(*qmp->resets), GFP_KERNEL);
60994a407ccSDmitry Baryshkov 	if (!qmp->resets)
61094a407ccSDmitry Baryshkov 		return -ENOMEM;
61194a407ccSDmitry Baryshkov 
612ccac0847SDmitry Baryshkov 	for (i = 0; i < cfg->num_resets; i++)
613ccac0847SDmitry Baryshkov 		qmp->resets[i].id = cfg->reset_list[i];
61494a407ccSDmitry Baryshkov 
615ccac0847SDmitry Baryshkov 	ret = devm_reset_control_bulk_get_exclusive(dev, cfg->num_resets, qmp->resets);
616ccac0847SDmitry Baryshkov 	if (ret)
617ccac0847SDmitry Baryshkov 		return dev_err_probe(dev, ret, "failed to get resets\n");
61894a407ccSDmitry Baryshkov 
61994a407ccSDmitry Baryshkov 	return 0;
62094a407ccSDmitry Baryshkov }
62194a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_clk_init(struct device * dev,const struct qmp_phy_cfg * cfg)622c577468cSJohan Hovold static int qmp_pcie_msm8996_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg)
62394a407ccSDmitry Baryshkov {
62494a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = dev_get_drvdata(dev);
62594a407ccSDmitry Baryshkov 	int num = cfg->num_clks;
62694a407ccSDmitry Baryshkov 	int i;
62794a407ccSDmitry Baryshkov 
62894a407ccSDmitry Baryshkov 	qmp->clks = devm_kcalloc(dev, num, sizeof(*qmp->clks), GFP_KERNEL);
62994a407ccSDmitry Baryshkov 	if (!qmp->clks)
63094a407ccSDmitry Baryshkov 		return -ENOMEM;
63194a407ccSDmitry Baryshkov 
63294a407ccSDmitry Baryshkov 	for (i = 0; i < num; i++)
63394a407ccSDmitry Baryshkov 		qmp->clks[i].id = cfg->clk_list[i];
63494a407ccSDmitry Baryshkov 
63594a407ccSDmitry Baryshkov 	return devm_clk_bulk_get(dev, num, qmp->clks);
63694a407ccSDmitry Baryshkov }
63794a407ccSDmitry Baryshkov 
phy_clk_release_provider(void * res)63894a407ccSDmitry Baryshkov static void phy_clk_release_provider(void *res)
63994a407ccSDmitry Baryshkov {
64094a407ccSDmitry Baryshkov 	of_clk_del_provider(res);
64194a407ccSDmitry Baryshkov }
64294a407ccSDmitry Baryshkov 
64394a407ccSDmitry Baryshkov /*
64494a407ccSDmitry Baryshkov  * Register a fixed rate pipe clock.
64594a407ccSDmitry Baryshkov  *
64694a407ccSDmitry Baryshkov  * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
64794a407ccSDmitry Baryshkov  * controls it. The <s>_pipe_clk coming out of the GCC is requested
64894a407ccSDmitry Baryshkov  * by the PHY driver for its operations.
64994a407ccSDmitry Baryshkov  * We register the <s>_pipe_clksrc here. The gcc driver takes care
65094a407ccSDmitry Baryshkov  * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
65194a407ccSDmitry Baryshkov  * Below picture shows this relationship.
65294a407ccSDmitry Baryshkov  *
65394a407ccSDmitry Baryshkov  *         +---------------+
65494a407ccSDmitry Baryshkov  *         |   PHY block   |<<---------------------------------------+
65594a407ccSDmitry Baryshkov  *         |               |                                         |
65694a407ccSDmitry Baryshkov  *         |   +-------+   |                   +-----+               |
65794a407ccSDmitry Baryshkov  *   I/P---^-->|  PLL  |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
65894a407ccSDmitry Baryshkov  *    clk  |   +-------+   |                   +-----+
65994a407ccSDmitry Baryshkov  *         +---------------+
66094a407ccSDmitry Baryshkov  */
phy_pipe_clk_register(struct qcom_qmp * qmp,struct device_node * np)66194a407ccSDmitry Baryshkov static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np)
66294a407ccSDmitry Baryshkov {
66394a407ccSDmitry Baryshkov 	struct clk_fixed_rate *fixed;
66494a407ccSDmitry Baryshkov 	struct clk_init_data init = { };
66594a407ccSDmitry Baryshkov 	int ret;
66694a407ccSDmitry Baryshkov 
66794a407ccSDmitry Baryshkov 	ret = of_property_read_string(np, "clock-output-names", &init.name);
66894a407ccSDmitry Baryshkov 	if (ret) {
66994a407ccSDmitry Baryshkov 		dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
67094a407ccSDmitry Baryshkov 		return ret;
67194a407ccSDmitry Baryshkov 	}
67294a407ccSDmitry Baryshkov 
67394a407ccSDmitry Baryshkov 	fixed = devm_kzalloc(qmp->dev, sizeof(*fixed), GFP_KERNEL);
67494a407ccSDmitry Baryshkov 	if (!fixed)
67594a407ccSDmitry Baryshkov 		return -ENOMEM;
67694a407ccSDmitry Baryshkov 
67794a407ccSDmitry Baryshkov 	init.ops = &clk_fixed_rate_ops;
67894a407ccSDmitry Baryshkov 
67994a407ccSDmitry Baryshkov 	/* controllers using QMP phys use 125MHz pipe clock interface */
68094a407ccSDmitry Baryshkov 	fixed->fixed_rate = 125000000;
68194a407ccSDmitry Baryshkov 	fixed->hw.init = &init;
68294a407ccSDmitry Baryshkov 
68394a407ccSDmitry Baryshkov 	ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
68494a407ccSDmitry Baryshkov 	if (ret)
68594a407ccSDmitry Baryshkov 		return ret;
68694a407ccSDmitry Baryshkov 
68794a407ccSDmitry Baryshkov 	ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
68894a407ccSDmitry Baryshkov 	if (ret)
68994a407ccSDmitry Baryshkov 		return ret;
69094a407ccSDmitry Baryshkov 
69194a407ccSDmitry Baryshkov 	/*
69294a407ccSDmitry Baryshkov 	 * Roll a devm action because the clock provider is the child node, but
69394a407ccSDmitry Baryshkov 	 * the child node is not actually a device.
69494a407ccSDmitry Baryshkov 	 */
69594a407ccSDmitry Baryshkov 	return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np);
69694a407ccSDmitry Baryshkov }
69794a407ccSDmitry Baryshkov 
698c577468cSJohan Hovold static const struct phy_ops qmp_pcie_msm8996_ops = {
699c577468cSJohan Hovold 	.power_on	= qmp_pcie_msm8996_enable,
700c577468cSJohan Hovold 	.power_off	= qmp_pcie_msm8996_disable,
70194a407ccSDmitry Baryshkov 	.owner		= THIS_MODULE,
70294a407ccSDmitry Baryshkov };
70394a407ccSDmitry Baryshkov 
qcom_qmp_reset_control_put(void * data)70494a407ccSDmitry Baryshkov static void qcom_qmp_reset_control_put(void *data)
70594a407ccSDmitry Baryshkov {
70694a407ccSDmitry Baryshkov 	reset_control_put(data);
70794a407ccSDmitry Baryshkov }
70894a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_create(struct device * dev,struct device_node * np,int id,void __iomem * serdes,const struct qmp_phy_cfg * cfg)709c577468cSJohan Hovold static int qmp_pcie_msm8996_create(struct device *dev, struct device_node *np, int id,
71094a407ccSDmitry Baryshkov 			void __iomem *serdes, const struct qmp_phy_cfg *cfg)
71194a407ccSDmitry Baryshkov {
71294a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp = dev_get_drvdata(dev);
71394a407ccSDmitry Baryshkov 	struct phy *generic_phy;
71494a407ccSDmitry Baryshkov 	struct qmp_phy *qphy;
71594a407ccSDmitry Baryshkov 	int ret;
71694a407ccSDmitry Baryshkov 
71794a407ccSDmitry Baryshkov 	qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
71894a407ccSDmitry Baryshkov 	if (!qphy)
71994a407ccSDmitry Baryshkov 		return -ENOMEM;
72094a407ccSDmitry Baryshkov 
72194a407ccSDmitry Baryshkov 	qphy->cfg = cfg;
72294a407ccSDmitry Baryshkov 	qphy->serdes = serdes;
72394a407ccSDmitry Baryshkov 	/*
7248d3bf724SJohan Hovold 	 * Get memory resources for each PHY:
72594a407ccSDmitry Baryshkov 	 * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2.
72694a407ccSDmitry Baryshkov 	 */
7271f69ededSJohan Hovold 	qphy->tx = devm_of_iomap(dev, np, 0, NULL);
7281f69ededSJohan Hovold 	if (IS_ERR(qphy->tx))
7291f69ededSJohan Hovold 		return PTR_ERR(qphy->tx);
73094a407ccSDmitry Baryshkov 
7311f69ededSJohan Hovold 	qphy->rx = devm_of_iomap(dev, np, 1, NULL);
7321f69ededSJohan Hovold 	if (IS_ERR(qphy->rx))
7331f69ededSJohan Hovold 		return PTR_ERR(qphy->rx);
73494a407ccSDmitry Baryshkov 
7351f69ededSJohan Hovold 	qphy->pcs = devm_of_iomap(dev, np, 2, NULL);
7361f69ededSJohan Hovold 	if (IS_ERR(qphy->pcs))
7371f69ededSJohan Hovold 		return PTR_ERR(qphy->pcs);
73894a407ccSDmitry Baryshkov 
739302db460SJohan Hovold 	qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL);
74094a407ccSDmitry Baryshkov 	if (IS_ERR(qphy->pipe_clk)) {
74136db6ce1SJohan Hovold 		return dev_err_probe(dev, PTR_ERR(qphy->pipe_clk),
74236db6ce1SJohan Hovold 				     "failed to get lane%d pipe clock\n", id);
74394a407ccSDmitry Baryshkov 	}
74494a407ccSDmitry Baryshkov 
7455337b248SJohan Hovold 	qphy->lane_rst = of_reset_control_get_exclusive_by_index(np, 0);
74694a407ccSDmitry Baryshkov 	if (IS_ERR(qphy->lane_rst)) {
74794a407ccSDmitry Baryshkov 		dev_err(dev, "failed to get lane%d reset\n", id);
74894a407ccSDmitry Baryshkov 		return PTR_ERR(qphy->lane_rst);
74994a407ccSDmitry Baryshkov 	}
75094a407ccSDmitry Baryshkov 	ret = devm_add_action_or_reset(dev, qcom_qmp_reset_control_put,
75194a407ccSDmitry Baryshkov 				       qphy->lane_rst);
75294a407ccSDmitry Baryshkov 	if (ret)
75394a407ccSDmitry Baryshkov 		return ret;
75494a407ccSDmitry Baryshkov 
755c577468cSJohan Hovold 	generic_phy = devm_phy_create(dev, np, &qmp_pcie_msm8996_ops);
75694a407ccSDmitry Baryshkov 	if (IS_ERR(generic_phy)) {
75794a407ccSDmitry Baryshkov 		ret = PTR_ERR(generic_phy);
75894a407ccSDmitry Baryshkov 		dev_err(dev, "failed to create qphy %d\n", ret);
75994a407ccSDmitry Baryshkov 		return ret;
76094a407ccSDmitry Baryshkov 	}
76194a407ccSDmitry Baryshkov 
76294a407ccSDmitry Baryshkov 	qphy->phy = generic_phy;
76394a407ccSDmitry Baryshkov 	qphy->index = id;
76494a407ccSDmitry Baryshkov 	qphy->qmp = qmp;
76594a407ccSDmitry Baryshkov 	qmp->phys[id] = qphy;
76694a407ccSDmitry Baryshkov 	phy_set_drvdata(generic_phy, qphy);
76794a407ccSDmitry Baryshkov 
76894a407ccSDmitry Baryshkov 	return 0;
76994a407ccSDmitry Baryshkov }
77094a407ccSDmitry Baryshkov 
771c577468cSJohan Hovold static const struct of_device_id qmp_pcie_msm8996_of_match_table[] = {
77294a407ccSDmitry Baryshkov 	{
77394a407ccSDmitry Baryshkov 		.compatible = "qcom,msm8996-qmp-pcie-phy",
77494a407ccSDmitry Baryshkov 		.data = &msm8996_pciephy_cfg,
77594a407ccSDmitry Baryshkov 	},
77694a407ccSDmitry Baryshkov 	{ },
77794a407ccSDmitry Baryshkov };
778c577468cSJohan Hovold MODULE_DEVICE_TABLE(of, qmp_pcie_msm8996_of_match_table);
77994a407ccSDmitry Baryshkov 
qmp_pcie_msm8996_probe(struct platform_device * pdev)780c577468cSJohan Hovold static int qmp_pcie_msm8996_probe(struct platform_device *pdev)
78194a407ccSDmitry Baryshkov {
78294a407ccSDmitry Baryshkov 	struct qcom_qmp *qmp;
78394a407ccSDmitry Baryshkov 	struct device *dev = &pdev->dev;
78494a407ccSDmitry Baryshkov 	struct device_node *child;
78594a407ccSDmitry Baryshkov 	struct phy_provider *phy_provider;
78694a407ccSDmitry Baryshkov 	void __iomem *serdes;
78794a407ccSDmitry Baryshkov 	const struct qmp_phy_cfg *cfg = NULL;
78894a407ccSDmitry Baryshkov 	int num, id, expected_phys;
78994a407ccSDmitry Baryshkov 	int ret;
79094a407ccSDmitry Baryshkov 
79194a407ccSDmitry Baryshkov 	qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL);
79294a407ccSDmitry Baryshkov 	if (!qmp)
79394a407ccSDmitry Baryshkov 		return -ENOMEM;
79494a407ccSDmitry Baryshkov 
79594a407ccSDmitry Baryshkov 	qmp->dev = dev;
79694a407ccSDmitry Baryshkov 	dev_set_drvdata(dev, qmp);
79794a407ccSDmitry Baryshkov 
79894a407ccSDmitry Baryshkov 	cfg = of_device_get_match_data(dev);
7999fc8fa59SDmitry Baryshkov 	if (!cfg)
80094a407ccSDmitry Baryshkov 		return -EINVAL;
80194a407ccSDmitry Baryshkov 
802f575ac2dSDmitry Baryshkov 	serdes = devm_platform_ioremap_resource(pdev, 0);
80394a407ccSDmitry Baryshkov 	if (IS_ERR(serdes))
80494a407ccSDmitry Baryshkov 		return PTR_ERR(serdes);
80594a407ccSDmitry Baryshkov 
8060d316ce5SJohan Hovold 	expected_phys = cfg->num_phys;
80794a407ccSDmitry Baryshkov 
80894a407ccSDmitry Baryshkov 	mutex_init(&qmp->phy_mutex);
80994a407ccSDmitry Baryshkov 
810c577468cSJohan Hovold 	ret = qmp_pcie_msm8996_clk_init(dev, cfg);
81194a407ccSDmitry Baryshkov 	if (ret)
81294a407ccSDmitry Baryshkov 		return ret;
81394a407ccSDmitry Baryshkov 
814c577468cSJohan Hovold 	ret = qmp_pcie_msm8996_reset_init(dev, cfg);
81594a407ccSDmitry Baryshkov 	if (ret)
81694a407ccSDmitry Baryshkov 		return ret;
81794a407ccSDmitry Baryshkov 
818c577468cSJohan Hovold 	ret = qmp_pcie_msm8996_vreg_init(dev, cfg);
819413e048aSYuan Can 	if (ret)
82028d74fc3SJohan Hovold 		return ret;
82194a407ccSDmitry Baryshkov 
82294a407ccSDmitry Baryshkov 	num = of_get_available_child_count(dev->of_node);
82394a407ccSDmitry Baryshkov 	/* do we have a rogue child node ? */
82494a407ccSDmitry Baryshkov 	if (num > expected_phys)
82594a407ccSDmitry Baryshkov 		return -EINVAL;
82694a407ccSDmitry Baryshkov 
82794a407ccSDmitry Baryshkov 	qmp->phys = devm_kcalloc(dev, num, sizeof(*qmp->phys), GFP_KERNEL);
82894a407ccSDmitry Baryshkov 	if (!qmp->phys)
82994a407ccSDmitry Baryshkov 		return -ENOMEM;
83094a407ccSDmitry Baryshkov 
83194a407ccSDmitry Baryshkov 	id = 0;
83294a407ccSDmitry Baryshkov 	for_each_available_child_of_node(dev->of_node, child) {
83394a407ccSDmitry Baryshkov 		/* Create per-lane phy */
834c577468cSJohan Hovold 		ret = qmp_pcie_msm8996_create(dev, child, id, serdes, cfg);
83594a407ccSDmitry Baryshkov 		if (ret) {
83694a407ccSDmitry Baryshkov 			dev_err(dev, "failed to create lane%d phy, %d\n",
83794a407ccSDmitry Baryshkov 				id, ret);
83894a407ccSDmitry Baryshkov 			goto err_node_put;
83994a407ccSDmitry Baryshkov 		}
84094a407ccSDmitry Baryshkov 
84194a407ccSDmitry Baryshkov 		/*
84294a407ccSDmitry Baryshkov 		 * Register the pipe clock provided by phy.
84394a407ccSDmitry Baryshkov 		 * See function description to see details of this pipe clock.
84494a407ccSDmitry Baryshkov 		 */
84594a407ccSDmitry Baryshkov 		ret = phy_pipe_clk_register(qmp, child);
84694a407ccSDmitry Baryshkov 		if (ret) {
84794a407ccSDmitry Baryshkov 			dev_err(qmp->dev,
84894a407ccSDmitry Baryshkov 				"failed to register pipe clock source\n");
84994a407ccSDmitry Baryshkov 			goto err_node_put;
85094a407ccSDmitry Baryshkov 		}
851f575ac2dSDmitry Baryshkov 
85294a407ccSDmitry Baryshkov 		id++;
85394a407ccSDmitry Baryshkov 	}
85494a407ccSDmitry Baryshkov 
85594a407ccSDmitry Baryshkov 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
85694a407ccSDmitry Baryshkov 
85794a407ccSDmitry Baryshkov 	return PTR_ERR_OR_ZERO(phy_provider);
85894a407ccSDmitry Baryshkov 
85994a407ccSDmitry Baryshkov err_node_put:
86094a407ccSDmitry Baryshkov 	of_node_put(child);
86194a407ccSDmitry Baryshkov 	return ret;
86294a407ccSDmitry Baryshkov }
86394a407ccSDmitry Baryshkov 
864c577468cSJohan Hovold static struct platform_driver qmp_pcie_msm8996_driver = {
865c577468cSJohan Hovold 	.probe		= qmp_pcie_msm8996_probe,
86694a407ccSDmitry Baryshkov 	.driver = {
8679fc8fa59SDmitry Baryshkov 		.name	= "qcom-qmp-msm8996-pcie-phy",
868c577468cSJohan Hovold 		.of_match_table = qmp_pcie_msm8996_of_match_table,
86994a407ccSDmitry Baryshkov 	},
87094a407ccSDmitry Baryshkov };
87194a407ccSDmitry Baryshkov 
872c577468cSJohan Hovold module_platform_driver(qmp_pcie_msm8996_driver);
87394a407ccSDmitry Baryshkov 
87494a407ccSDmitry Baryshkov MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>");
8759fc8fa59SDmitry Baryshkov MODULE_DESCRIPTION("Qualcomm QMP MSM8996 PCIe PHY driver");
87694a407ccSDmitry Baryshkov MODULE_LICENSE("GPL v2");
877