aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig7
-rw-r--r--drivers/phy/Makefile3
-rw-r--r--drivers/phy/phy-qcom-ufs-i.h159
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-14nm.c201
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-14nm.h177
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-20nm.c257
-rw-r--r--drivers/phy/phy-qcom-ufs-qmp-20nm.h235
-rw-r--r--drivers/phy/phy-qcom-ufs.c745
8 files changed, 1784 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index b24500afba25..2962de205ba7 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -284,4 +284,11 @@ config PHY_STIH41X_USB
284 Enable this to support the USB transceiver that is part of 284 Enable this to support the USB transceiver that is part of
285 STMicroelectronics STiH41x SoC series. 285 STMicroelectronics STiH41x SoC series.
286 286
287config PHY_QCOM_UFS
288 tristate "Qualcomm UFS PHY driver"
289 depends on OF && ARCH_MSM
290 select GENERIC_PHY
291 help
292 Support for UFS PHY on QCOM chipsets.
293
287endmenu 294endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 48bf5a15f161..f080e1bb2a74 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -35,3 +35,6 @@ obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
35obj-$(CONFIG_PHY_XGENE) += phy-xgene.o 35obj-$(CONFIG_PHY_XGENE) += phy-xgene.o
36obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o 36obj-$(CONFIG_PHY_STIH407_USB) += phy-stih407-usb.o
37obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o 37obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o
38obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
39obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o
40obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o
diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h
new file mode 100644
index 000000000000..591a39175e8a
--- /dev/null
+++ b/drivers/phy/phy-qcom-ufs-i.h
@@ -0,0 +1,159 @@
1/*
2 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#ifndef UFS_QCOM_PHY_I_H_
16#define UFS_QCOM_PHY_I_H_
17
18#include <linux/module.h>
19#include <linux/clk.h>
20#include <linux/regulator/consumer.h>
21#include <linux/slab.h>
22#include <linux/phy/phy-qcom-ufs.h>
23#include <linux/platform_device.h>
24#include <linux/io.h>
25#include <linux/delay.h>
26
27#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
28({ \
29 ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
30 might_sleep_if(timeout_us); \
31 for (;;) { \
32 (val) = readl(addr); \
33 if (cond) \
34 break; \
35 if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
36 (val) = readl(addr); \
37 break; \
38 } \
39 if (sleep_us) \
40 usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
41 } \
42 (cond) ? 0 : -ETIMEDOUT; \
43})
44
45#define UFS_QCOM_PHY_CAL_ENTRY(reg, val) \
46 { \
47 .reg_offset = reg, \
48 .cfg_value = val, \
49 }
50
51#define UFS_QCOM_PHY_NAME_LEN 30
52
53enum {
54 MASK_SERDES_START = 0x1,
55 MASK_PCS_READY = 0x1,
56};
57
58enum {
59 OFFSET_SERDES_START = 0x0,
60};
61
62struct ufs_qcom_phy_stored_attributes {
63 u32 att;
64 u32 value;
65};
66
67
68struct ufs_qcom_phy_calibration {
69 u32 reg_offset;
70 u32 cfg_value;
71};
72
73struct ufs_qcom_phy_vreg {
74 const char *name;
75 struct regulator *reg;
76 int max_uA;
77 int min_uV;
78 int max_uV;
79 bool enabled;
80 bool is_always_on;
81};
82
83struct ufs_qcom_phy {
84 struct list_head list;
85 struct device *dev;
86 void __iomem *mmio;
87 void __iomem *dev_ref_clk_ctrl_mmio;
88 struct clk *tx_iface_clk;
89 struct clk *rx_iface_clk;
90 bool is_iface_clk_enabled;
91 struct clk *ref_clk_src;
92 struct clk *ref_clk_parent;
93 struct clk *ref_clk;
94 bool is_ref_clk_enabled;
95 bool is_dev_ref_clk_enabled;
96 struct ufs_qcom_phy_vreg vdda_pll;
97 struct ufs_qcom_phy_vreg vdda_phy;
98 struct ufs_qcom_phy_vreg vddp_ref_clk;
99 unsigned int quirks;
100
101 /*
102 * If UFS link is put into Hibern8 and if UFS PHY analog hardware is
103 * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8
104 * exit might fail even after powering on UFS PHY analog hardware.
105 * Enabling this quirk will help to solve above issue by doing
106 * custom PHY settings just before PHY analog power collapse.
107 */
108 #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE BIT(0)
109
110 u8 host_ctrl_rev_major;
111 u16 host_ctrl_rev_minor;
112 u16 host_ctrl_rev_step;
113
114 char name[UFS_QCOM_PHY_NAME_LEN];
115 struct ufs_qcom_phy_calibration *cached_regs;
116 int cached_regs_table_size;
117 bool is_powered_on;
118 struct ufs_qcom_phy_specific_ops *phy_spec_ops;
119};
120
121/**
122 * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a
123 * specific implementation per phy. Each UFS phy, should implement
124 * those functions according to its spec and requirements
125 * @calibrate_phy: pointer to a function that calibrate the phy
126 * @start_serdes: pointer to a function that starts the serdes
127 * @is_physical_coding_sublayer_ready: pointer to a function that
128 * checks pcs readiness. returns 0 for success and non-zero for error.
129 * @set_tx_lane_enable: pointer to a function that enable tx lanes
130 * @power_control: pointer to a function that controls analog rail of phy
131 * and writes to QSERDES_RX_SIGDET_CNTRL attribute
132 */
133struct ufs_qcom_phy_specific_ops {
134 int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
135 void (*start_serdes)(struct ufs_qcom_phy *phy);
136 int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
137 void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
138 void (*power_control)(struct ufs_qcom_phy *phy, bool val);
139};
140
141struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
142int ufs_qcom_phy_power_on(struct phy *generic_phy);
143int ufs_qcom_phy_power_off(struct phy *generic_phy);
144int ufs_qcom_phy_exit(struct phy *generic_phy);
145int ufs_qcom_phy_init_clks(struct phy *generic_phy,
146 struct ufs_qcom_phy *phy_common);
147int ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
148 struct ufs_qcom_phy *phy_common);
149int ufs_qcom_phy_remove(struct phy *generic_phy,
150 struct ufs_qcom_phy *ufs_qcom_phy);
151struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
152 struct ufs_qcom_phy *common_cfg,
153 struct phy_ops *ufs_qcom_phy_gen_ops,
154 struct ufs_qcom_phy_specific_ops *phy_spec_ops);
155int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
156 struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A,
157 struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B,
158 bool is_rate_B);
159#endif
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c
new file mode 100644
index 000000000000..f5fc50a9fce7
--- /dev/null
+++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.c
@@ -0,0 +1,201 @@
1/*
2 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include "phy-qcom-ufs-qmp-14nm.h"
16
17#define UFS_PHY_NAME "ufs_phy_qmp_14nm"
18#define UFS_PHY_VDDA_PHY_UV (925000)
19
20static
21int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
22 bool is_rate_B)
23{
24 int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
25 int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
26 int err;
27
28 err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A,
29 tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B);
30
31 if (err)
32 dev_err(ufs_qcom_phy->dev,
33 "%s: ufs_qcom_phy_calibrate() failed %d\n",
34 __func__, err);
35 return err;
36}
37
38static
39void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
40{
41 phy_common->quirks =
42 UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
43}
44
45static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
46{
47 struct ufs_qcom_phy_qmp_14nm *phy = phy_get_drvdata(generic_phy);
48 struct ufs_qcom_phy *phy_common = &phy->common_cfg;
49 int err;
50
51 err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
52 if (err) {
53 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
54 __func__, err);
55 goto out;
56 }
57
58 err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
59 if (err) {
60 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
61 __func__, err);
62 goto out;
63 }
64 phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV;
65 phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV;
66
67 ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common);
68
69out:
70 return err;
71}
72
73static
74void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val)
75{
76 writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
77 /*
78 * Before any transactions involving PHY, ensure PHY knows
79 * that it's analog rail is powered ON (or OFF).
80 */
81 mb();
82}
83
84static inline
85void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
86{
87 /*
88 * 14nm PHY does not have TX_LANE_ENABLE register.
89 * Implement this function so as not to propagate error to caller.
90 */
91}
92
93static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy)
94{
95 u32 tmp;
96
97 tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
98 tmp &= ~MASK_SERDES_START;
99 tmp |= (1 << OFFSET_SERDES_START);
100 writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
101 /* Ensure register value is committed */
102 mb();
103}
104
105static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
106{
107 int err = 0;
108 u32 val;
109
110 err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
111 val, (val & MASK_PCS_READY), 10, 1000000);
112 if (err)
113 dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
114 __func__, err);
115 return err;
116}
117
118static struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
119 .init = ufs_qcom_phy_qmp_14nm_init,
120 .exit = ufs_qcom_phy_exit,
121 .power_on = ufs_qcom_phy_power_on,
122 .power_off = ufs_qcom_phy_power_off,
123 .owner = THIS_MODULE,
124};
125
126static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
127 .calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate,
128 .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
129 .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
130 .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
131 .power_control = ufs_qcom_phy_qmp_14nm_power_control,
132};
133
134static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev)
135{
136 struct device *dev = &pdev->dev;
137 struct phy *generic_phy;
138 struct ufs_qcom_phy_qmp_14nm *phy;
139 int err = 0;
140
141 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
142 if (!phy) {
143 dev_err(dev, "%s: failed to allocate phy\n", __func__);
144 err = -ENOMEM;
145 goto out;
146 }
147
148 generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
149 &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops);
150
151 if (!generic_phy) {
152 dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
153 __func__);
154 err = -EIO;
155 goto out;
156 }
157
158 phy_set_drvdata(generic_phy, phy);
159
160 strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
161 sizeof(phy->common_cfg.name));
162
163out:
164 return err;
165}
166
167static int ufs_qcom_phy_qmp_14nm_remove(struct platform_device *pdev)
168{
169 struct device *dev = &pdev->dev;
170 struct phy *generic_phy = to_phy(dev);
171 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
172 int err = 0;
173
174 err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
175 if (err)
176 dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
177 __func__, err);
178
179 return err;
180}
181
182static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = {
183 {.compatible = "qcom,ufs-phy-qmp-14nm"},
184 {},
185};
186MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match);
187
188static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = {
189 .probe = ufs_qcom_phy_qmp_14nm_probe,
190 .remove = ufs_qcom_phy_qmp_14nm_remove,
191 .driver = {
192 .of_match_table = ufs_qcom_phy_qmp_14nm_of_match,
193 .name = "ufs_qcom_phy_qmp_14nm",
194 .owner = THIS_MODULE,
195 },
196};
197
198module_platform_driver(ufs_qcom_phy_qmp_14nm_driver);
199
200MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm");
201MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h
new file mode 100644
index 000000000000..3aefdbacbcd0
--- /dev/null
+++ b/drivers/phy/phy-qcom-ufs-qmp-14nm.h
@@ -0,0 +1,177 @@
1/*
2 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#ifndef UFS_QCOM_PHY_QMP_14NM_H_
16#define UFS_QCOM_PHY_QMP_14NM_H_
17
18#include "phy-qcom-ufs-i.h"
19
20/* QCOM UFS PHY control registers */
21#define COM_OFF(x) (0x000 + x)
22#define PHY_OFF(x) (0xC00 + x)
23#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
24#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
25
26/* UFS PHY QSERDES COM registers */
27#define QSERDES_COM_BG_TIMER COM_OFF(0x0C)
28#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x34)
29#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x3C)
30#define QSERDES_COM_LOCK_CMP1_MODE0 COM_OFF(0x4C)
31#define QSERDES_COM_LOCK_CMP2_MODE0 COM_OFF(0x50)
32#define QSERDES_COM_LOCK_CMP3_MODE0 COM_OFF(0x54)
33#define QSERDES_COM_LOCK_CMP1_MODE1 COM_OFF(0x58)
34#define QSERDES_COM_LOCK_CMP2_MODE1 COM_OFF(0x5C)
35#define QSERDES_COM_LOCK_CMP3_MODE1 COM_OFF(0x60)
36#define QSERDES_COM_CP_CTRL_MODE0 COM_OFF(0x78)
37#define QSERDES_COM_CP_CTRL_MODE1 COM_OFF(0x7C)
38#define QSERDES_COM_PLL_RCTRL_MODE0 COM_OFF(0x84)
39#define QSERDES_COM_PLL_RCTRL_MODE1 COM_OFF(0x88)
40#define QSERDES_COM_PLL_CCTRL_MODE0 COM_OFF(0x90)
41#define QSERDES_COM_PLL_CCTRL_MODE1 COM_OFF(0x94)
42#define QSERDES_COM_SYSCLK_EN_SEL COM_OFF(0xAC)
43#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0xB4)
44#define QSERDES_COM_LOCK_CMP_EN COM_OFF(0xC8)
45#define QSERDES_COM_LOCK_CMP_CFG COM_OFF(0xCC)
46#define QSERDES_COM_DEC_START_MODE0 COM_OFF(0xD0)
47#define QSERDES_COM_DEC_START_MODE1 COM_OFF(0xD4)
48#define QSERDES_COM_DIV_FRAC_START1_MODE0 COM_OFF(0xDC)
49#define QSERDES_COM_DIV_FRAC_START2_MODE0 COM_OFF(0xE0)
50#define QSERDES_COM_DIV_FRAC_START3_MODE0 COM_OFF(0xE4)
51#define QSERDES_COM_DIV_FRAC_START1_MODE1 COM_OFF(0xE8)
52#define QSERDES_COM_DIV_FRAC_START2_MODE1 COM_OFF(0xEC)
53#define QSERDES_COM_DIV_FRAC_START3_MODE1 COM_OFF(0xF0)
54#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 COM_OFF(0x108)
55#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 COM_OFF(0x10C)
56#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 COM_OFF(0x110)
57#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 COM_OFF(0x114)
58#define QSERDES_COM_VCO_TUNE_CTRL COM_OFF(0x124)
59#define QSERDES_COM_VCO_TUNE_MAP COM_OFF(0x128)
60#define QSERDES_COM_VCO_TUNE1_MODE0 COM_OFF(0x12C)
61#define QSERDES_COM_VCO_TUNE2_MODE0 COM_OFF(0x130)
62#define QSERDES_COM_VCO_TUNE1_MODE1 COM_OFF(0x134)
63#define QSERDES_COM_VCO_TUNE2_MODE1 COM_OFF(0x138)
64#define QSERDES_COM_VCO_TUNE_TIMER1 COM_OFF(0x144)
65#define QSERDES_COM_VCO_TUNE_TIMER2 COM_OFF(0x148)
66#define QSERDES_COM_CLK_SELECT COM_OFF(0x174)
67#define QSERDES_COM_HSCLK_SEL COM_OFF(0x178)
68#define QSERDES_COM_CORECLK_DIV COM_OFF(0x184)
69#define QSERDES_COM_CORE_CLK_EN COM_OFF(0x18C)
70#define QSERDES_COM_CMN_CONFIG COM_OFF(0x194)
71#define QSERDES_COM_SVS_MODE_CLK_SEL COM_OFF(0x19C)
72#define QSERDES_COM_CORECLK_DIV_MODE1 COM_OFF(0x1BC)
73
74/* UFS PHY registers */
75#define UFS_PHY_PHY_START PHY_OFF(0x00)
76#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x04)
77#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x168)
78
79/* UFS PHY TX registers */
80#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN TX_OFF(0, 0x68)
81#define QSERDES_TX_LANE_MODE TX_OFF(0, 0x94)
82
83/* UFS PHY RX registers */
84#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN RX_OFF(0, 0x40)
85#define QSERDES_RX_RX_TERM_BW RX_OFF(0, 0x90)
86#define QSERDES_RX_RX_EQ_GAIN1_LSB RX_OFF(0, 0xC4)
87#define QSERDES_RX_RX_EQ_GAIN1_MSB RX_OFF(0, 0xC8)
88#define QSERDES_RX_RX_EQ_GAIN2_LSB RX_OFF(0, 0xCC)
89#define QSERDES_RX_RX_EQ_GAIN2_MSB RX_OFF(0, 0xD0)
90#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 RX_OFF(0, 0xD8)
91#define QSERDES_RX_SIGDET_CNTRL RX_OFF(0, 0x114)
92#define QSERDES_RX_SIGDET_LVL RX_OFF(0, 0x118)
93#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL RX_OFF(0, 0x11C)
94#define QSERDES_RX_RX_INTERFACE_MODE RX_OFF(0, 0x12C)
95
96/*
97 * This structure represents the 14nm specific phy.
98 * common_cfg MUST remain the first field in this structure
99 * in case extra fields are added. This way, when calling
100 * get_ufs_qcom_phy() of generic phy, we can extract the
101 * common phy structure (struct ufs_qcom_phy) out of it
102 * regardless of the relevant specific phy.
103 */
104struct ufs_qcom_phy_qmp_14nm {
105 struct ufs_qcom_phy common_cfg;
106};
107
108static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
109 UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
110 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e),
111 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7),
112 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30),
113 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06),
114 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
115 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a),
116 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05),
117 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a),
118 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a),
119 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01),
120 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10),
121 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20),
122 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00),
123 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00),
124 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
125 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
126 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14),
127 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
128 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82),
129 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00),
130 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00),
131 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00),
132 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
133 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
134 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
135 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
136 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
137 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28),
138 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02),
139 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff),
140 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c),
141 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
142 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98),
143 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00),
144 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00),
145 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00),
146 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b),
147 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
148 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28),
149 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80),
150 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
151 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6),
152 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
153 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
154 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f),
155 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
156
157 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45),
158 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02),
159
160 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24),
161 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02),
162 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
163 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18),
164 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B),
165 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B),
166 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF),
167 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F),
168 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF),
169 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F),
170 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
171};
172
173static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
174 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54),
175};
176
177#endif
diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c
new file mode 100644
index 000000000000..8332f96b2c4a
--- /dev/null
+++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.c
@@ -0,0 +1,257 @@
1/*
2 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include "phy-qcom-ufs-qmp-20nm.h"
16
17#define UFS_PHY_NAME "ufs_phy_qmp_20nm"
18
19static
20int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
21 bool is_rate_B)
22{
23 struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
24 int tbl_size_A, tbl_size_B;
25 u8 major = ufs_qcom_phy->host_ctrl_rev_major;
26 u16 minor = ufs_qcom_phy->host_ctrl_rev_minor;
27 u16 step = ufs_qcom_phy->host_ctrl_rev_step;
28 int err;
29
30 if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) {
31 tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0);
32 tbl_A = phy_cal_table_rate_A_1_2_0;
33 } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) {
34 tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0);
35 tbl_A = phy_cal_table_rate_A_1_3_0;
36 } else {
37 dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n",
38 __func__);
39 err = -ENODEV;
40 goto out;
41 }
42
43 tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
44 tbl_B = phy_cal_table_rate_B;
45
46 err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A,
47 tbl_B, tbl_size_B, is_rate_B);
48
49 if (err)
50 dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n",
51 __func__, err);
52
53out:
54 return err;
55}
56
57static
58void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
59{
60 phy_common->quirks =
61 UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
62}
63
64static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
65{
66 struct ufs_qcom_phy_qmp_20nm *phy = phy_get_drvdata(generic_phy);
67 struct ufs_qcom_phy *phy_common = &phy->common_cfg;
68 int err = 0;
69
70 err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
71 if (err) {
72 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
73 __func__, err);
74 goto out;
75 }
76
77 err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
78 if (err) {
79 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
80 __func__, err);
81 goto out;
82 }
83
84 ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common);
85
86out:
87 return err;
88}
89
90static
91void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val)
92{
93 bool hibern8_exit_after_pwr_collapse = phy->quirks &
94 UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
95
96 if (val) {
97 writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
98 /*
99 * Before any transactions involving PHY, ensure PHY knows
100 * that it's analog rail is powered ON.
101 */
102 mb();
103
104 if (hibern8_exit_after_pwr_collapse) {
105 /*
106 * Give atleast 1us delay after restoring PHY analog
107 * power.
108 */
109 usleep_range(1, 2);
110 writel_relaxed(0x0A, phy->mmio +
111 QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
112 writel_relaxed(0x08, phy->mmio +
113 QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
114 /*
115 * Make sure workaround is deactivated before proceeding
116 * with normal PHY operations.
117 */
118 mb();
119 }
120 } else {
121 if (hibern8_exit_after_pwr_collapse) {
122 writel_relaxed(0x0A, phy->mmio +
123 QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
124 writel_relaxed(0x02, phy->mmio +
125 QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
126 /*
127 * Make sure that above workaround is activated before
128 * PHY analog power collapse.
129 */
130 mb();
131 }
132
133 writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
134 /*
135 * ensure that PHY knows its PHY analog rail is going
136 * to be powered down
137 */
138 mb();
139 }
140}
141
142static
143void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
144{
145 writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK,
146 phy->mmio + UFS_PHY_TX_LANE_ENABLE);
147 mb();
148}
149
150static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy)
151{
152 u32 tmp;
153
154 tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
155 tmp &= ~MASK_SERDES_START;
156 tmp |= (1 << OFFSET_SERDES_START);
157 writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
158 mb();
159}
160
161static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
162{
163 int err = 0;
164 u32 val;
165
166 err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
167 val, (val & MASK_PCS_READY), 10, 1000000);
168 if (err)
169 dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
170 __func__, err);
171 return err;
172}
173
174static struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
175 .init = ufs_qcom_phy_qmp_20nm_init,
176 .exit = ufs_qcom_phy_exit,
177 .power_on = ufs_qcom_phy_power_on,
178 .power_off = ufs_qcom_phy_power_off,
179 .owner = THIS_MODULE,
180};
181
182static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
183 .calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate,
184 .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
185 .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
186 .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
187 .power_control = ufs_qcom_phy_qmp_20nm_power_control,
188};
189
190static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev)
191{
192 struct device *dev = &pdev->dev;
193 struct phy *generic_phy;
194 struct ufs_qcom_phy_qmp_20nm *phy;
195 int err = 0;
196
197 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
198 if (!phy) {
199 dev_err(dev, "%s: failed to allocate phy\n", __func__);
200 err = -ENOMEM;
201 goto out;
202 }
203
204 generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
205 &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops);
206
207 if (!generic_phy) {
208 dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
209 __func__);
210 err = -EIO;
211 goto out;
212 }
213
214 phy_set_drvdata(generic_phy, phy);
215
216 strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
217 sizeof(phy->common_cfg.name));
218
219out:
220 return err;
221}
222
223static int ufs_qcom_phy_qmp_20nm_remove(struct platform_device *pdev)
224{
225 struct device *dev = &pdev->dev;
226 struct phy *generic_phy = to_phy(dev);
227 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
228 int err = 0;
229
230 err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
231 if (err)
232 dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
233 __func__, err);
234
235 return err;
236}
237
238static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = {
239 {.compatible = "qcom,ufs-phy-qmp-20nm"},
240 {},
241};
242MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match);
243
244static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = {
245 .probe = ufs_qcom_phy_qmp_20nm_probe,
246 .remove = ufs_qcom_phy_qmp_20nm_remove,
247 .driver = {
248 .of_match_table = ufs_qcom_phy_qmp_20nm_of_match,
249 .name = "ufs_qcom_phy_qmp_20nm",
250 .owner = THIS_MODULE,
251 },
252};
253
254module_platform_driver(ufs_qcom_phy_qmp_20nm_driver);
255
256MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm");
257MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/phy-qcom-ufs-qmp-20nm.h
new file mode 100644
index 000000000000..4f3076bb3d71
--- /dev/null
+++ b/drivers/phy/phy-qcom-ufs-qmp-20nm.h
@@ -0,0 +1,235 @@
1/*
2 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#ifndef UFS_QCOM_PHY_QMP_20NM_H_
16#define UFS_QCOM_PHY_QMP_20NM_H_
17
18#include "phy-qcom-ufs-i.h"
19
20/* QCOM UFS PHY control registers */
21
22#define COM_OFF(x) (0x000 + x)
23#define PHY_OFF(x) (0xC00 + x)
24#define TX_OFF(n, x) (0x400 + (0x400 * n) + x)
25#define RX_OFF(n, x) (0x600 + (0x400 * n) + x)
26
27/* UFS PHY PLL block registers */
28#define QSERDES_COM_SYS_CLK_CTRL COM_OFF(0x0)
29#define QSERDES_COM_PLL_VCOTAIL_EN COM_OFF(0x04)
30#define QSERDES_COM_PLL_CNTRL COM_OFF(0x14)
31#define QSERDES_COM_PLL_IP_SETI COM_OFF(0x24)
32#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL COM_OFF(0x28)
33#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN COM_OFF(0x30)
34#define QSERDES_COM_PLL_CP_SETI COM_OFF(0x34)
35#define QSERDES_COM_PLL_IP_SETP COM_OFF(0x38)
36#define QSERDES_COM_PLL_CP_SETP COM_OFF(0x3C)
37#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND COM_OFF(0x48)
38#define QSERDES_COM_RESETSM_CNTRL COM_OFF(0x4C)
39#define QSERDES_COM_RESETSM_CNTRL2 COM_OFF(0x50)
40#define QSERDES_COM_PLLLOCK_CMP1 COM_OFF(0x90)
41#define QSERDES_COM_PLLLOCK_CMP2 COM_OFF(0x94)
42#define QSERDES_COM_PLLLOCK_CMP3 COM_OFF(0x98)
43#define QSERDES_COM_PLLLOCK_CMP_EN COM_OFF(0x9C)
44#define QSERDES_COM_BGTC COM_OFF(0xA0)
45#define QSERDES_COM_DEC_START1 COM_OFF(0xAC)
46#define QSERDES_COM_PLL_AMP_OS COM_OFF(0xB0)
47#define QSERDES_COM_RES_CODE_UP_OFFSET COM_OFF(0xD8)
48#define QSERDES_COM_RES_CODE_DN_OFFSET COM_OFF(0xDC)
49#define QSERDES_COM_DIV_FRAC_START1 COM_OFF(0x100)
50#define QSERDES_COM_DIV_FRAC_START2 COM_OFF(0x104)
51#define QSERDES_COM_DIV_FRAC_START3 COM_OFF(0x108)
52#define QSERDES_COM_DEC_START2 COM_OFF(0x10C)
53#define QSERDES_COM_PLL_RXTXEPCLK_EN COM_OFF(0x110)
54#define QSERDES_COM_PLL_CRCTRL COM_OFF(0x114)
55#define QSERDES_COM_PLL_CLKEPDIV COM_OFF(0x118)
56
57/* TX LANE n (0, 1) registers */
58#define QSERDES_TX_EMP_POST1_LVL(n) TX_OFF(n, 0x08)
59#define QSERDES_TX_DRV_LVL(n) TX_OFF(n, 0x0C)
60#define QSERDES_TX_LANE_MODE(n) TX_OFF(n, 0x54)
61
62/* RX LANE n (0, 1) registers */
63#define QSERDES_RX_CDR_CONTROL1(n) RX_OFF(n, 0x0)
64#define QSERDES_RX_CDR_CONTROL_HALF(n) RX_OFF(n, 0x8)
65#define QSERDES_RX_RX_EQ_GAIN1_LSB(n) RX_OFF(n, 0xA8)
66#define QSERDES_RX_RX_EQ_GAIN1_MSB(n) RX_OFF(n, 0xAC)
67#define QSERDES_RX_RX_EQ_GAIN2_LSB(n) RX_OFF(n, 0xB0)
68#define QSERDES_RX_RX_EQ_GAIN2_MSB(n) RX_OFF(n, 0xB4)
69#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n) RX_OFF(n, 0xBC)
70#define QSERDES_RX_CDR_CONTROL_QUARTER(n) RX_OFF(n, 0xC)
71#define QSERDES_RX_SIGDET_CNTRL(n) RX_OFF(n, 0x100)
72
73/* UFS PHY registers */
74#define UFS_PHY_PHY_START PHY_OFF(0x00)
75#define UFS_PHY_POWER_DOWN_CONTROL PHY_OFF(0x4)
76#define UFS_PHY_TX_LANE_ENABLE PHY_OFF(0x44)
77#define UFS_PHY_PWM_G1_CLK_DIVIDER PHY_OFF(0x08)
78#define UFS_PHY_PWM_G2_CLK_DIVIDER PHY_OFF(0x0C)
79#define UFS_PHY_PWM_G3_CLK_DIVIDER PHY_OFF(0x10)
80#define UFS_PHY_PWM_G4_CLK_DIVIDER PHY_OFF(0x14)
81#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER PHY_OFF(0x34)
82#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER PHY_OFF(0x38)
83#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER PHY_OFF(0x3C)
84#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER PHY_OFF(0x40)
85#define UFS_PHY_OMC_STATUS_RDVAL PHY_OFF(0x68)
86#define UFS_PHY_LINE_RESET_TIME PHY_OFF(0x28)
87#define UFS_PHY_LINE_RESET_GRANULARITY PHY_OFF(0x2C)
88#define UFS_PHY_TSYNC_RSYNC_CNTL PHY_OFF(0x48)
89#define UFS_PHY_PLL_CNTL PHY_OFF(0x50)
90#define UFS_PHY_TX_LARGE_AMP_DRV_LVL PHY_OFF(0x54)
91#define UFS_PHY_TX_SMALL_AMP_DRV_LVL PHY_OFF(0x5C)
92#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL PHY_OFF(0x58)
93#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL PHY_OFF(0x60)
94#define UFS_PHY_CFG_CHANGE_CNT_VAL PHY_OFF(0x64)
95#define UFS_PHY_RX_SYNC_WAIT_TIME PHY_OFF(0x6C)
96#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB4)
97#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE0)
98#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xB8)
99#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY PHY_OFF(0xE4)
100#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xBC)
101#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY PHY_OFF(0xE8)
102#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC)
103#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY PHY_OFF(0x100)
104#define UFS_PHY_RX_SIGDET_CTRL3 PHY_OFF(0x14c)
105#define UFS_PHY_RMMI_ATTR_CTRL PHY_OFF(0x160)
106#define UFS_PHY_RMMI_RX_CFGUPDT_L1 (1 << 7)
107#define UFS_PHY_RMMI_TX_CFGUPDT_L1 (1 << 6)
108#define UFS_PHY_RMMI_CFGWR_L1 (1 << 5)
109#define UFS_PHY_RMMI_CFGRD_L1 (1 << 4)
110#define UFS_PHY_RMMI_RX_CFGUPDT_L0 (1 << 3)
111#define UFS_PHY_RMMI_TX_CFGUPDT_L0 (1 << 2)
112#define UFS_PHY_RMMI_CFGWR_L0 (1 << 1)
113#define UFS_PHY_RMMI_CFGRD_L0 (1 << 0)
114#define UFS_PHY_RMMI_ATTRID PHY_OFF(0x164)
115#define UFS_PHY_RMMI_ATTRWRVAL PHY_OFF(0x168)
116#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS PHY_OFF(0x16C)
117#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS PHY_OFF(0x170)
118#define UFS_PHY_PCS_READY_STATUS PHY_OFF(0x174)
119
120#define UFS_PHY_TX_LANE_ENABLE_MASK 0x3
121
122/*
123 * This structure represents the 20nm specific phy.
124 * common_cfg MUST remain the first field in this structure
125 * in case extra fields are added. This way, when calling
126 * get_ufs_qcom_phy() of generic phy, we can extract the
127 * common phy structure (struct ufs_qcom_phy) out of it
128 * regardless of the relevant specific phy.
129 */
130struct ufs_qcom_phy_qmp_20nm {
131 struct ufs_qcom_phy common_cfg;
132};
133
134static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = {
135 UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
136 UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
137 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
138 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
139 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
140 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
141 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
142 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
143 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
144 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
145 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
146 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
147 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
148 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
149 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
150 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
151 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
152 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
153 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
154 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
155 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
156 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
157 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
158 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
159 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
160 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
161 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
162 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
163 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
164 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
165 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
166 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
167 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
168 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b),
169 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f),
170 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
171 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F),
172 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20),
173 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F),
174 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20),
175 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
176 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
177 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
178 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
179 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
180};
181
182static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = {
183 UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
184 UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
185 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
186 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
187 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
188 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
189 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
190 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
191 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
192 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
193 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
194 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
195 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
196 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
197 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
198 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
199 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
200 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
201 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
202 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
203 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
204 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
205 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
206 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
207 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
208 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
209 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
210 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
211 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
212 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
213 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
214 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
215 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b),
216 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38),
217 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c),
218 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02),
219 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02),
220 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
221 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40),
222 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
223 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
224 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
225 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
226 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
227};
228
229static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
230 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
231 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65),
232 UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e),
233};
234
235#endif
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
new file mode 100644
index 000000000000..44ee983d57fe
--- /dev/null
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -0,0 +1,745 @@
1/*
2 * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#include "phy-qcom-ufs-i.h"
16
17#define MAX_PROP_NAME 32
18#define VDDA_PHY_MIN_UV 1000000
19#define VDDA_PHY_MAX_UV 1000000
20#define VDDA_PLL_MIN_UV 1800000
21#define VDDA_PLL_MAX_UV 1800000
22#define VDDP_REF_CLK_MIN_UV 1200000
23#define VDDP_REF_CLK_MAX_UV 1200000
24
25static int __ufs_qcom_phy_init_vreg(struct phy *, struct ufs_qcom_phy_vreg *,
26 const char *, bool);
27static int ufs_qcom_phy_init_vreg(struct phy *, struct ufs_qcom_phy_vreg *,
28 const char *);
29static int ufs_qcom_phy_base_init(struct platform_device *pdev,
30 struct ufs_qcom_phy *phy_common);
31
32int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
33 struct ufs_qcom_phy_calibration *tbl_A,
34 int tbl_size_A,
35 struct ufs_qcom_phy_calibration *tbl_B,
36 int tbl_size_B, bool is_rate_B)
37{
38 int i;
39 int ret = 0;
40
41 if (!tbl_A) {
42 dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__);
43 ret = EINVAL;
44 goto out;
45 }
46
47 for (i = 0; i < tbl_size_A; i++)
48 writel_relaxed(tbl_A[i].cfg_value,
49 ufs_qcom_phy->mmio + tbl_A[i].reg_offset);
50
51 /*
52 * In case we would like to work in rate B, we need
53 * to override a registers that were configured in rate A table
54 * with registers of rate B table.
55 * table.
56 */
57 if (is_rate_B) {
58 if (!tbl_B) {
59 dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL",
60 __func__);
61 ret = EINVAL;
62 goto out;
63 }
64
65 for (i = 0; i < tbl_size_B; i++)
66 writel_relaxed(tbl_B[i].cfg_value,
67 ufs_qcom_phy->mmio + tbl_B[i].reg_offset);
68 }
69
70 /* flush buffered writes */
71 mb();
72
73out:
74 return ret;
75}
76
77struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
78 struct ufs_qcom_phy *common_cfg,
79 struct phy_ops *ufs_qcom_phy_gen_ops,
80 struct ufs_qcom_phy_specific_ops *phy_spec_ops)
81{
82 int err;
83 struct device *dev = &pdev->dev;
84 struct phy *generic_phy = NULL;
85 struct phy_provider *phy_provider;
86
87 err = ufs_qcom_phy_base_init(pdev, common_cfg);
88 if (err) {
89 dev_err(dev, "%s: phy base init failed %d\n", __func__, err);
90 goto out;
91 }
92
93 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
94 if (IS_ERR(phy_provider)) {
95 err = PTR_ERR(phy_provider);
96 dev_err(dev, "%s: failed to register phy %d\n", __func__, err);
97 goto out;
98 }
99
100 generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops);
101 if (IS_ERR(generic_phy)) {
102 err = PTR_ERR(generic_phy);
103 dev_err(dev, "%s: failed to create phy %d\n", __func__, err);
104 goto out;
105 }
106
107 common_cfg->phy_spec_ops = phy_spec_ops;
108 common_cfg->dev = dev;
109
110out:
111 return generic_phy;
112}
113
114/*
115 * This assumes the embedded phy structure inside generic_phy is of type
116 * struct ufs_qcom_phy. In order to function properly it's crucial
117 * to keep the embedded struct "struct ufs_qcom_phy common_cfg"
118 * as the first inside generic_phy.
119 */
120struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy)
121{
122 return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy);
123}
124
125static
126int ufs_qcom_phy_base_init(struct platform_device *pdev,
127 struct ufs_qcom_phy *phy_common)
128{
129 struct device *dev = &pdev->dev;
130 struct resource *res;
131 int err = 0;
132
133 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
134 if (!res) {
135 dev_err(dev, "%s: phy_mem resource not found\n", __func__);
136 err = -ENOMEM;
137 goto out;
138 }
139
140 phy_common->mmio = devm_ioremap_resource(dev, res);
141 if (IS_ERR((void const *)phy_common->mmio)) {
142 err = PTR_ERR((void const *)phy_common->mmio);
143 phy_common->mmio = NULL;
144 dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
145 __func__, err);
146 goto out;
147 }
148
149 /* "dev_ref_clk_ctrl_mem" is optional resource */
150 res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
151 "dev_ref_clk_ctrl_mem");
152 if (!res) {
153 dev_dbg(dev, "%s: dev_ref_clk_ctrl_mem resource not found\n",
154 __func__);
155 goto out;
156 }
157
158 phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
159 if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio)) {
160 err = PTR_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio);
161 phy_common->dev_ref_clk_ctrl_mmio = NULL;
162 dev_err(dev, "%s: ioremap for dev_ref_clk_ctrl_mem resource failed %d\n",
163 __func__, err);
164 }
165
166out:
167 return err;
168}
169
170static int __ufs_qcom_phy_clk_get(struct phy *phy,
171 const char *name, struct clk **clk_out, bool err_print)
172{
173 struct clk *clk;
174 int err = 0;
175 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
176 struct device *dev = ufs_qcom_phy->dev;
177
178 clk = devm_clk_get(dev, name);
179 if (IS_ERR(clk)) {
180 err = PTR_ERR(clk);
181 if (err_print)
182 dev_err(dev, "failed to get %s err %d", name, err);
183 } else {
184 *clk_out = clk;
185 }
186
187 return err;
188}
189
190static
191int ufs_qcom_phy_clk_get(struct phy *phy,
192 const char *name, struct clk **clk_out)
193{
194 return __ufs_qcom_phy_clk_get(phy, name, clk_out, true);
195}
196
197int
198ufs_qcom_phy_init_clks(struct phy *generic_phy,
199 struct ufs_qcom_phy *phy_common)
200{
201 int err;
202
203 err = ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk",
204 &phy_common->tx_iface_clk);
205 if (err)
206 goto out;
207
208 err = ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk",
209 &phy_common->rx_iface_clk);
210 if (err)
211 goto out;
212
213 err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk_src",
214 &phy_common->ref_clk_src);
215 if (err)
216 goto out;
217
218 /*
219 * "ref_clk_parent" is optional hence don't abort init if it's not
220 * found.
221 */
222 __ufs_qcom_phy_clk_get(generic_phy, "ref_clk_parent",
223 &phy_common->ref_clk_parent, false);
224
225 err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk",
226 &phy_common->ref_clk);
227
228out:
229 return err;
230}
231
232int
233ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
234 struct ufs_qcom_phy *phy_common)
235{
236 int err;
237
238 err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_pll,
239 "vdda-pll");
240 if (err)
241 goto out;
242
243 err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_phy,
244 "vdda-phy");
245
246 if (err)
247 goto out;
248
249 /* vddp-ref-clk-* properties are optional */
250 __ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vddp_ref_clk,
251 "vddp-ref-clk", true);
252out:
253 return err;
254}
255
256static int __ufs_qcom_phy_init_vreg(struct phy *phy,
257 struct ufs_qcom_phy_vreg *vreg, const char *name, bool optional)
258{
259 int err = 0;
260 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
261 struct device *dev = ufs_qcom_phy->dev;
262
263 char prop_name[MAX_PROP_NAME];
264
265 vreg->name = kstrdup(name, GFP_KERNEL);
266 if (!vreg->name) {
267 err = -ENOMEM;
268 goto out;
269 }
270
271 vreg->reg = devm_regulator_get(dev, name);
272 if (IS_ERR(vreg->reg)) {
273 err = PTR_ERR(vreg->reg);
274 vreg->reg = NULL;
275 if (!optional)
276 dev_err(dev, "failed to get %s, %d\n", name, err);
277 goto out;
278 }
279
280 if (dev->of_node) {
281 snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name);
282 err = of_property_read_u32(dev->of_node,
283 prop_name, &vreg->max_uA);
284 if (err && err != -EINVAL) {
285 dev_err(dev, "%s: failed to read %s\n",
286 __func__, prop_name);
287 goto out;
288 } else if (err == -EINVAL || !vreg->max_uA) {
289 if (regulator_count_voltages(vreg->reg) > 0) {
290 dev_err(dev, "%s: %s is mandatory\n",
291 __func__, prop_name);
292 goto out;
293 }
294 err = 0;
295 }
296 snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name);
297 if (of_get_property(dev->of_node, prop_name, NULL))
298 vreg->is_always_on = true;
299 else
300 vreg->is_always_on = false;
301 }
302
303 if (!strcmp(name, "vdda-pll")) {
304 vreg->max_uV = VDDA_PLL_MAX_UV;
305 vreg->min_uV = VDDA_PLL_MIN_UV;
306 } else if (!strcmp(name, "vdda-phy")) {
307 vreg->max_uV = VDDA_PHY_MAX_UV;
308 vreg->min_uV = VDDA_PHY_MIN_UV;
309 } else if (!strcmp(name, "vddp-ref-clk")) {
310 vreg->max_uV = VDDP_REF_CLK_MAX_UV;
311 vreg->min_uV = VDDP_REF_CLK_MIN_UV;
312 }
313
314out:
315 if (err)
316 kfree(vreg->name);
317 return err;
318}
319
320static int ufs_qcom_phy_init_vreg(struct phy *phy,
321 struct ufs_qcom_phy_vreg *vreg, const char *name)
322{
323 return __ufs_qcom_phy_init_vreg(phy, vreg, name, false);
324}
325
326static
327int ufs_qcom_phy_cfg_vreg(struct phy *phy,
328 struct ufs_qcom_phy_vreg *vreg, bool on)
329{
330 int ret = 0;
331 struct regulator *reg = vreg->reg;
332 const char *name = vreg->name;
333 int min_uV;
334 int uA_load;
335 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
336 struct device *dev = ufs_qcom_phy->dev;
337
338 BUG_ON(!vreg);
339
340 if (regulator_count_voltages(reg) > 0) {
341 min_uV = on ? vreg->min_uV : 0;
342 ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
343 if (ret) {
344 dev_err(dev, "%s: %s set voltage failed, err=%d\n",
345 __func__, name, ret);
346 goto out;
347 }
348 uA_load = on ? vreg->max_uA : 0;
349 ret = regulator_set_optimum_mode(reg, uA_load);
350 if (ret >= 0) {
351 /*
352 * regulator_set_optimum_mode() returns new regulator
353 * mode upon success.
354 */
355 ret = 0;
356 } else {
357 dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
358 __func__, name, uA_load, ret);
359 goto out;
360 }
361 }
362out:
363 return ret;
364}
365
366static
367int ufs_qcom_phy_enable_vreg(struct phy *phy,
368 struct ufs_qcom_phy_vreg *vreg)
369{
370 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
371 struct device *dev = ufs_qcom_phy->dev;
372 int ret = 0;
373
374 if (!vreg || vreg->enabled)
375 goto out;
376
377 ret = ufs_qcom_phy_cfg_vreg(phy, vreg, true);
378 if (ret) {
379 dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n",
380 __func__, ret);
381 goto out;
382 }
383
384 ret = regulator_enable(vreg->reg);
385 if (ret) {
386 dev_err(dev, "%s: enable failed, err=%d\n",
387 __func__, ret);
388 goto out;
389 }
390
391 vreg->enabled = true;
392out:
393 return ret;
394}
395
396int ufs_qcom_phy_enable_ref_clk(struct phy *generic_phy)
397{
398 int ret = 0;
399 struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
400
401 if (phy->is_ref_clk_enabled)
402 goto out;
403
404 /*
405 * reference clock is propagated in a daisy-chained manner from
406 * source to phy, so ungate them at each stage.
407 */
408 ret = clk_prepare_enable(phy->ref_clk_src);
409 if (ret) {
410 dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n",
411 __func__, ret);
412 goto out;
413 }
414
415 /*
416 * "ref_clk_parent" is optional clock hence make sure that clk reference
417 * is available before trying to enable the clock.
418 */
419 if (phy->ref_clk_parent) {
420 ret = clk_prepare_enable(phy->ref_clk_parent);
421 if (ret) {
422 dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n",
423 __func__, ret);
424 goto out_disable_src;
425 }
426 }
427
428 ret = clk_prepare_enable(phy->ref_clk);
429 if (ret) {
430 dev_err(phy->dev, "%s: ref_clk enable failed %d\n",
431 __func__, ret);
432 goto out_disable_parent;
433 }
434
435 phy->is_ref_clk_enabled = true;
436 goto out;
437
438out_disable_parent:
439 if (phy->ref_clk_parent)
440 clk_disable_unprepare(phy->ref_clk_parent);
441out_disable_src:
442 clk_disable_unprepare(phy->ref_clk_src);
443out:
444 return ret;
445}
446
447static
448int ufs_qcom_phy_disable_vreg(struct phy *phy,
449 struct ufs_qcom_phy_vreg *vreg)
450{
451 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
452 struct device *dev = ufs_qcom_phy->dev;
453 int ret = 0;
454
455 if (!vreg || !vreg->enabled || vreg->is_always_on)
456 goto out;
457
458 ret = regulator_disable(vreg->reg);
459
460 if (!ret) {
461 /* ignore errors on applying disable config */
462 ufs_qcom_phy_cfg_vreg(phy, vreg, false);
463 vreg->enabled = false;
464 } else {
465 dev_err(dev, "%s: %s disable failed, err=%d\n",
466 __func__, vreg->name, ret);
467 }
468out:
469 return ret;
470}
471
472void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy)
473{
474 struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
475
476 if (phy->is_ref_clk_enabled) {
477 clk_disable_unprepare(phy->ref_clk);
478 /*
479 * "ref_clk_parent" is optional clock hence make sure that clk
480 * reference is available before trying to disable the clock.
481 */
482 if (phy->ref_clk_parent)
483 clk_disable_unprepare(phy->ref_clk_parent);
484 clk_disable_unprepare(phy->ref_clk_src);
485 phy->is_ref_clk_enabled = false;
486 }
487}
488
489#define UFS_REF_CLK_EN (1 << 5)
490
491static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable)
492{
493 struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
494
495 if (phy->dev_ref_clk_ctrl_mmio &&
496 (enable ^ phy->is_dev_ref_clk_enabled)) {
497 u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio);
498
499 if (enable)
500 temp |= UFS_REF_CLK_EN;
501 else
502 temp &= ~UFS_REF_CLK_EN;
503
504 /*
505 * If we are here to disable this clock immediately after
506 * entering into hibern8, we need to make sure that device
507 * ref_clk is active atleast 1us after the hibern8 enter.
508 */
509 if (!enable)
510 udelay(1);
511
512 writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio);
513 /* ensure that ref_clk is enabled/disabled before we return */
514 wmb();
515 /*
516 * If we call hibern8 exit after this, we need to make sure that
517 * device ref_clk is stable for atleast 1us before the hibern8
518 * exit command.
519 */
520 if (enable)
521 udelay(1);
522
523 phy->is_dev_ref_clk_enabled = enable;
524 }
525}
526
527void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy)
528{
529 ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true);
530}
531
532void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy)
533{
534 ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false);
535}
536
537/* Turn ON M-PHY RMMI interface clocks */
538int ufs_qcom_phy_enable_iface_clk(struct phy *generic_phy)
539{
540 struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
541 int ret = 0;
542
543 if (phy->is_iface_clk_enabled)
544 goto out;
545
546 ret = clk_prepare_enable(phy->tx_iface_clk);
547 if (ret) {
548 dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n",
549 __func__, ret);
550 goto out;
551 }
552 ret = clk_prepare_enable(phy->rx_iface_clk);
553 if (ret) {
554 clk_disable_unprepare(phy->tx_iface_clk);
555 dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n",
556 __func__, ret);
557 goto out;
558 }
559 phy->is_iface_clk_enabled = true;
560
561out:
562 return ret;
563}
564
565/* Turn OFF M-PHY RMMI interface clocks */
566void ufs_qcom_phy_disable_iface_clk(struct phy *generic_phy)
567{
568 struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
569
570 if (phy->is_iface_clk_enabled) {
571 clk_disable_unprepare(phy->tx_iface_clk);
572 clk_disable_unprepare(phy->rx_iface_clk);
573 phy->is_iface_clk_enabled = false;
574 }
575}
576
577int ufs_qcom_phy_start_serdes(struct phy *generic_phy)
578{
579 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
580 int ret = 0;
581
582 if (!ufs_qcom_phy->phy_spec_ops->start_serdes) {
583 dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n",
584 __func__);
585 ret = -ENOTSUPP;
586 } else {
587 ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy);
588 }
589
590 return ret;
591}
592
593int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
594{
595 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
596 int ret = 0;
597
598 if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) {
599 dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n",
600 __func__);
601 ret = -ENOTSUPP;
602 } else {
603 ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy,
604 tx_lanes);
605 }
606
607 return ret;
608}
609
610void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
611 u8 major, u16 minor, u16 step)
612{
613 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
614
615 ufs_qcom_phy->host_ctrl_rev_major = major;
616 ufs_qcom_phy->host_ctrl_rev_minor = minor;
617 ufs_qcom_phy->host_ctrl_rev_step = step;
618}
619
620int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B)
621{
622 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
623 int ret = 0;
624
625 if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) {
626 dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n",
627 __func__);
628 ret = -ENOTSUPP;
629 } else {
630 ret = ufs_qcom_phy->phy_spec_ops->
631 calibrate_phy(ufs_qcom_phy, is_rate_B);
632 if (ret)
633 dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n",
634 __func__, ret);
635 }
636
637 return ret;
638}
639
640int ufs_qcom_phy_remove(struct phy *generic_phy,
641 struct ufs_qcom_phy *ufs_qcom_phy)
642{
643 phy_power_off(generic_phy);
644
645 kfree(ufs_qcom_phy->vdda_pll.name);
646 kfree(ufs_qcom_phy->vdda_phy.name);
647
648 return 0;
649}
650
651int ufs_qcom_phy_exit(struct phy *generic_phy)
652{
653 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
654
655 if (ufs_qcom_phy->is_powered_on)
656 phy_power_off(generic_phy);
657
658 return 0;
659}
660
661int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
662{
663 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
664
665 if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) {
666 dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n",
667 __func__);
668 return -ENOTSUPP;
669 }
670
671 return ufs_qcom_phy->phy_spec_ops->
672 is_physical_coding_sublayer_ready(ufs_qcom_phy);
673}
674
675int ufs_qcom_phy_power_on(struct phy *generic_phy)
676{
677 struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
678 struct device *dev = phy_common->dev;
679 int err;
680
681 err = ufs_qcom_phy_enable_vreg(generic_phy, &phy_common->vdda_phy);
682 if (err) {
683 dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
684 __func__, err);
685 goto out;
686 }
687
688 phy_common->phy_spec_ops->power_control(phy_common, true);
689
690 /* vdda_pll also enables ref clock LDOs so enable it first */
691 err = ufs_qcom_phy_enable_vreg(generic_phy, &phy_common->vdda_pll);
692 if (err) {
693 dev_err(dev, "%s enable vdda_pll failed, err=%d\n",
694 __func__, err);
695 goto out_disable_phy;
696 }
697
698 err = ufs_qcom_phy_enable_ref_clk(generic_phy);
699 if (err) {
700 dev_err(dev, "%s enable phy ref clock failed, err=%d\n",
701 __func__, err);
702 goto out_disable_pll;
703 }
704
705 /* enable device PHY ref_clk pad rail */
706 if (phy_common->vddp_ref_clk.reg) {
707 err = ufs_qcom_phy_enable_vreg(generic_phy,
708 &phy_common->vddp_ref_clk);
709 if (err) {
710 dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n",
711 __func__, err);
712 goto out_disable_ref_clk;
713 }
714 }
715
716 phy_common->is_powered_on = true;
717 goto out;
718
719out_disable_ref_clk:
720 ufs_qcom_phy_disable_ref_clk(generic_phy);
721out_disable_pll:
722 ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
723out_disable_phy:
724 ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
725out:
726 return err;
727}
728
729int ufs_qcom_phy_power_off(struct phy *generic_phy)
730{
731 struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
732
733 phy_common->phy_spec_ops->power_control(phy_common, false);
734
735 if (phy_common->vddp_ref_clk.reg)
736 ufs_qcom_phy_disable_vreg(generic_phy,
737 &phy_common->vddp_ref_clk);
738 ufs_qcom_phy_disable_ref_clk(generic_phy);
739
740 ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
741 ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
742 phy_common->is_powered_on = false;
743
744 return 0;
745}