blob: 0bfde0c720331ffc5c4f3dc093b9f165507dbd4d [file] [log] [blame]
Subhash Jadavanicce6fbc2016-08-11 11:35:26 -07001/*
Subhash Jadavani9c807702017-04-01 00:35:51 -07002 * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
Subhash Jadavanicce6fbc2016-08-11 11:35:26 -07003 *
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-v3.h"
16
17#define UFS_PHY_NAME "ufs_phy_qmp_v3"
18
19static
20int ufs_qcom_phy_qmp_v3_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
21 bool is_rate_B)
22{
Subhash Jadavani8bfeb632017-04-03 17:14:30 -070023 /*
24 * Writing PHY calibration in this order:
25 * 1. Write Rate-A calibration first (1-lane mode).
26 * 2. Write 2nd lane configuration if needed.
27 * 3. Write Rate-B calibration overrides
28 */
29 ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_rate_A,
30 ARRAY_SIZE(phy_cal_table_rate_A));
31 if (ufs_qcom_phy->lanes_per_direction == 2)
32 ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_2nd_lane,
33 ARRAY_SIZE(phy_cal_table_2nd_lane));
34 if (is_rate_B)
35 ufs_qcom_phy_write_tbl(ufs_qcom_phy, phy_cal_table_rate_B,
36 ARRAY_SIZE(phy_cal_table_rate_B));
37 /* flush buffered writes */
38 mb();
Subhash Jadavanicce6fbc2016-08-11 11:35:26 -070039
Subhash Jadavani8bfeb632017-04-03 17:14:30 -070040 return 0;
Subhash Jadavanicce6fbc2016-08-11 11:35:26 -070041}
42
43static int ufs_qcom_phy_qmp_v3_init(struct phy *generic_phy)
44{
45 struct ufs_qcom_phy_qmp_v3 *phy = phy_get_drvdata(generic_phy);
46 struct ufs_qcom_phy *phy_common = &phy->common_cfg;
47 int err;
48
49 err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
50 if (err) {
51 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
52 __func__, err);
53 goto out;
54 }
55
56 err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
57 if (err) {
58 dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
59 __func__, err);
60 goto out;
61 }
62
63out:
64 return err;
65}
66
67static
68void ufs_qcom_phy_qmp_v3_power_control(struct ufs_qcom_phy *phy,
69 bool power_ctrl)
70{
71 if (!power_ctrl) {
72 /* apply analog power collapse */
73 writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
74 /*
75 * Make sure that PHY knows its analog rail is going to be
76 * powered OFF.
77 */
78 mb();
79 } else {
80 /* bring PHY out of analog power collapse */
81 writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
82
83 /*
84 * Before any transactions involving PHY, ensure PHY knows
85 * that it's analog rail is powered ON.
86 */
87 mb();
88 }
89}
90
91static inline
92void ufs_qcom_phy_qmp_v3_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
93{
94 /*
95 * v3 PHY does not have TX_LANE_ENABLE register.
96 * Implement this function so as not to propagate error to caller.
97 */
98}
99
100static
101void ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg(struct ufs_qcom_phy *phy, bool ctrl)
102{
103 u32 temp;
104
105 temp = readl_relaxed(phy->mmio + UFS_PHY_LINECFG_DISABLE);
106
107 if (ctrl) /* enable RX LineCfg */
108 temp &= ~UFS_PHY_RX_LINECFG_DISABLE_BIT;
109 else /* disable RX LineCfg */
110 temp |= UFS_PHY_RX_LINECFG_DISABLE_BIT;
111
112 writel_relaxed(temp, phy->mmio + UFS_PHY_LINECFG_DISABLE);
113 /* make sure that RX LineCfg config applied before we return */
114 mb();
115}
116
117static inline void ufs_qcom_phy_qmp_v3_start_serdes(struct ufs_qcom_phy *phy)
118{
119 u32 tmp;
120
121 tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
122 tmp &= ~MASK_SERDES_START;
123 tmp |= (1 << OFFSET_SERDES_START);
124 writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
125 /* Ensure register value is committed */
126 mb();
127}
128
129static int ufs_qcom_phy_qmp_v3_is_pcs_ready(struct ufs_qcom_phy *phy_common)
130{
131 int err = 0;
132 u32 val;
133
134 err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
135 val, (val & MASK_PCS_READY), 10, 1000000);
136 if (err) {
137 dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
138 __func__, err);
139 goto out;
140 }
141
142out:
143 return err;
144}
145
Subhash Jadavani9c807702017-04-01 00:35:51 -0700146static void ufs_qcom_phy_qmp_v3_dbg_register_dump(struct ufs_qcom_phy *phy)
147{
148 ufs_qcom_phy_dump_regs(phy, COM_BASE, COM_SIZE,
149 "PHY QSERDES COM Registers ");
150 ufs_qcom_phy_dump_regs(phy, PHY_BASE, PHY_SIZE,
151 "PHY Registers ");
152 ufs_qcom_phy_dump_regs(phy, RX_BASE(0), RX_SIZE,
153 "PHY RX0 Registers ");
154 ufs_qcom_phy_dump_regs(phy, TX_BASE(0), TX_SIZE,
155 "PHY TX0 Registers ");
156 ufs_qcom_phy_dump_regs(phy, RX_BASE(1), RX_SIZE,
157 "PHY RX1 Registers ");
158 ufs_qcom_phy_dump_regs(phy, TX_BASE(1), TX_SIZE,
159 "PHY TX1 Registers ");
160}
161
Subhash Jadavanicce6fbc2016-08-11 11:35:26 -0700162struct phy_ops ufs_qcom_phy_qmp_v3_phy_ops = {
163 .init = ufs_qcom_phy_qmp_v3_init,
164 .exit = ufs_qcom_phy_exit,
165 .power_on = ufs_qcom_phy_power_on,
166 .power_off = ufs_qcom_phy_power_off,
167 .owner = THIS_MODULE,
168};
169
170struct ufs_qcom_phy_specific_ops phy_v3_ops = {
171 .calibrate_phy = ufs_qcom_phy_qmp_v3_phy_calibrate,
172 .start_serdes = ufs_qcom_phy_qmp_v3_start_serdes,
173 .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_v3_is_pcs_ready,
174 .set_tx_lane_enable = ufs_qcom_phy_qmp_v3_set_tx_lane_enable,
175 .ctrl_rx_linecfg = ufs_qcom_phy_qmp_v3_ctrl_rx_linecfg,
176 .power_control = ufs_qcom_phy_qmp_v3_power_control,
Subhash Jadavani9c807702017-04-01 00:35:51 -0700177 .dbg_register_dump = ufs_qcom_phy_qmp_v3_dbg_register_dump,
Subhash Jadavanicce6fbc2016-08-11 11:35:26 -0700178};
179
180static int ufs_qcom_phy_qmp_v3_probe(struct platform_device *pdev)
181{
182 struct device *dev = &pdev->dev;
183 struct phy *generic_phy;
184 struct ufs_qcom_phy_qmp_v3 *phy;
185 int err = 0;
186
187 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
188 if (!phy) {
189 err = -ENOMEM;
190 goto out;
191 }
192
193 generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
194 &ufs_qcom_phy_qmp_v3_phy_ops, &phy_v3_ops);
195
196 if (!generic_phy) {
197 dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
198 __func__);
199 err = -EIO;
200 goto out;
201 }
202
203 phy_set_drvdata(generic_phy, phy);
204
205 strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
206 sizeof(phy->common_cfg.name));
207
208out:
209 return err;
210}
211
212static int ufs_qcom_phy_qmp_v3_remove(struct platform_device *pdev)
213{
214 struct device *dev = &pdev->dev;
215 struct phy *generic_phy = to_phy(dev);
216 struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
217 int err = 0;
218
219 err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
220 if (err)
221 dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
222 __func__, err);
223
224 return err;
225}
226
227static const struct of_device_id ufs_qcom_phy_qmp_v3_of_match[] = {
228 {.compatible = "qcom,ufs-phy-qmp-v3"},
229 {},
230};
231MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_v3_of_match);
232
233static struct platform_driver ufs_qcom_phy_qmp_v3_driver = {
234 .probe = ufs_qcom_phy_qmp_v3_probe,
235 .remove = ufs_qcom_phy_qmp_v3_remove,
236 .driver = {
237 .of_match_table = ufs_qcom_phy_qmp_v3_of_match,
238 .name = "ufs_qcom_phy_qmp_v3",
239 .owner = THIS_MODULE,
240 },
241};
242
243module_platform_driver(ufs_qcom_phy_qmp_v3_driver);
244
245MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP v3");
246MODULE_LICENSE("GPL v2");