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