blob: eaeae83b999fdc16315923909407a82253b81095 [file] [log] [blame]
Bean Huo67351112020-06-05 22:05:19 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Vinayak Holikatti03b17812013-02-26 18:04:45 +05302/*
3 * Universal Flash Storage Host controller Platform bus based glue driver
Vinayak Holikatti03b17812013-02-26 18:04:45 +05304 * Copyright (C) 2011-2013 Samsung India Software Operations
5 *
6 * Authors:
7 * Santosh Yaraganavi <santosh.sy@samsung.com>
8 * Vinayak Holikatti <h.vinayak@samsung.com>
Vinayak Holikatti03b17812013-02-26 18:04:45 +05309 */
10
Vinayak Holikatti03b17812013-02-26 18:04:45 +053011#include <linux/platform_device.h>
Sujit Reddy Thumma62694732013-07-30 00:36:00 +053012#include <linux/pm_runtime.h>
Sujit Reddy Thumma5c0c28a2014-09-25 15:32:21 +030013#include <linux/of.h>
Vinayak Holikatti03b17812013-02-26 18:04:45 +053014
Seungwon Jeon2953f852013-06-27 13:31:54 +090015#include "ufshcd.h"
Yaniv Gardi47555a52015-10-28 13:15:49 +020016#include "ufshcd-pltfrm.h"
Stanley Chu58b60a92019-03-16 13:04:41 +080017#include "unipro.h"
Sujit Reddy Thumma5c0c28a2014-09-25 15:32:21 +030018
Yaniv Gardi54b879b2016-03-10 17:37:05 +020019#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
20
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030021static int ufshcd_parse_clock_info(struct ufs_hba *hba)
22{
23 int ret = 0;
24 int cnt;
25 int i;
26 struct device *dev = hba->dev;
27 struct device_node *np = dev->of_node;
28 char *name;
29 u32 *clkfreq = NULL;
30 struct ufs_clk_info *clki;
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030031 int len = 0;
32 size_t sz = 0;
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030033
34 if (!np)
35 goto out;
36
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030037 cnt = of_property_count_strings(np, "clock-names");
38 if (!cnt || (cnt == -EINVAL)) {
39 dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
40 __func__);
41 } else if (cnt < 0) {
42 dev_err(dev, "%s: count clock strings failed, err %d\n",
43 __func__, cnt);
44 ret = cnt;
45 }
46
47 if (cnt <= 0)
48 goto out;
49
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030050 if (!of_get_property(np, "freq-table-hz", &len)) {
51 dev_info(dev, "freq-table-hz property not specified\n");
52 goto out;
53 }
54
55 if (len <= 0)
56 goto out;
57
58 sz = len / sizeof(*clkfreq);
59 if (sz != 2 * cnt) {
60 dev_err(dev, "%s len mismatch\n", "freq-table-hz");
61 ret = -EINVAL;
62 goto out;
63 }
64
Kees Cooka86854d2018-06-12 14:07:58 -070065 clkfreq = devm_kcalloc(dev, sz, sizeof(*clkfreq),
66 GFP_KERNEL);
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030067 if (!clkfreq) {
68 ret = -ENOMEM;
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030069 goto out;
70 }
71
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030072 ret = of_property_read_u32_array(np, "freq-table-hz",
73 clkfreq, sz);
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030074 if (ret && (ret != -EINVAL)) {
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030075 dev_err(dev, "%s: error reading array %d\n",
76 "freq-table-hz", ret);
Dolev Ravive8cb64d2014-10-23 13:25:17 +030077 return ret;
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030078 }
79
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030080 for (i = 0; i < sz; i += 2) {
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030081 ret = of_property_read_string_index(np,
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030082 "clock-names", i/2, (const char **)&name);
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030083 if (ret)
Dolev Ravive8cb64d2014-10-23 13:25:17 +030084 goto out;
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030085
86 clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
87 if (!clki) {
88 ret = -ENOMEM;
Dolev Ravive8cb64d2014-10-23 13:25:17 +030089 goto out;
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030090 }
91
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030092 clki->min_freq = clkfreq[i];
93 clki->max_freq = clkfreq[i+1];
Srinivas Kandagatlab6ca7702021-09-14 10:22:14 +010094 clki->name = devm_kstrdup(dev, name, GFP_KERNEL);
Can Guo81309c22020-11-25 18:01:00 -080095 if (!strcmp(name, "ref_clk"))
96 clki->keep_link_active = true;
Sahitya Tummala4cff6d992014-09-25 15:32:33 +030097 dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz",
98 clki->min_freq, clki->max_freq, clki->name);
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +030099 list_add_tail(&clki->list, &hba->clk_list_head);
100 }
Sahitya Tummala4cff6d992014-09-25 15:32:33 +0300101out:
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +0300102 return ret;
103}
104
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300105#define MAX_PROP_SIZE 32
106static int ufshcd_populate_vreg(struct device *dev, const char *name,
107 struct ufs_vreg **out_vreg)
108{
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300109 char prop_name[MAX_PROP_SIZE];
110 struct ufs_vreg *vreg = NULL;
111 struct device_node *np = dev->of_node;
112
113 if (!np) {
114 dev_err(dev, "%s: non DT initialization\n", __func__);
115 goto out;
116 }
117
118 snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
119 if (!of_parse_phandle(np, prop_name, 0)) {
120 dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
121 __func__, prop_name);
122 goto out;
123 }
124
125 vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
Dolev Raviv758581b2014-10-23 13:25:15 +0300126 if (!vreg)
127 return -ENOMEM;
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300128
Srinivas Kandagatlab6ca7702021-09-14 10:22:14 +0100129 vreg->name = devm_kstrdup(dev, name, GFP_KERNEL);
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300130
131 snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
Stanley Chu12e3ffb2019-03-28 17:16:26 +0800132 if (of_property_read_u32(np, prop_name, &vreg->max_uA)) {
133 dev_info(dev, "%s: unable to find %s\n", __func__, prop_name);
134 vreg->max_uA = 0;
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300135 }
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300136out:
Yue Hu2a8561b2021-03-10 16:27:41 +0800137 *out_vreg = vreg;
138 return 0;
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300139}
140
141/**
142 * ufshcd_parse_regulator_info - get regulator info from device tree
143 * @hba: per adapter instance
144 *
145 * Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
146 * If any of the supplies are not defined it is assumed that they are always-on
147 * and hence return zero. If the property is defined but parsing is failed
148 * then return corresponding error.
149 */
150static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
151{
152 int err;
153 struct device *dev = hba->dev;
154 struct ufs_vreg_info *info = &hba->vreg_info;
155
Raviv Shvili6a771a62014-09-25 15:32:24 +0300156 err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba);
157 if (err)
158 goto out;
159
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300160 err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
161 if (err)
162 goto out;
163
164 err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
165 if (err)
166 goto out;
167
168 err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
169out:
170 return err;
171}
172
Yaniv Gardi47555a52015-10-28 13:15:49 +0200173void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
Subhash Jadavani57d104c2014-09-25 15:32:30 +0300174{
175 ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
176}
Yaniv Gardi47555a52015-10-28 13:15:49 +0200177EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown);
Subhash Jadavani57d104c2014-09-25 15:32:30 +0300178
Yaniv Gardi54b879b2016-03-10 17:37:05 +0200179static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
180{
181 struct device *dev = hba->dev;
182 int ret;
183
184 ret = of_property_read_u32(dev->of_node, "lanes-per-direction",
185 &hba->lanes_per_direction);
186 if (ret) {
187 dev_dbg(hba->dev,
188 "%s: failed to read lanes-per-direction, ret=%d\n",
189 __func__, ret);
190 hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION;
191 }
192}
193
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530194/**
Stanley Chu58b60a92019-03-16 13:04:41 +0800195 * ufshcd_get_pwr_dev_param - get finally agreed attributes for
196 * power mode change
197 * @pltfrm_param: pointer to platform parameters
198 * @dev_max: pointer to device attributes
199 * @agreed_pwr: returned agreed attributes
200 *
201 * Returns 0 on success, non-zero value on failure
202 */
203int ufshcd_get_pwr_dev_param(struct ufs_dev_params *pltfrm_param,
204 struct ufs_pa_layer_attr *dev_max,
205 struct ufs_pa_layer_attr *agreed_pwr)
206{
207 int min_pltfrm_gear;
208 int min_dev_gear;
209 bool is_dev_sup_hs = false;
210 bool is_pltfrm_max_hs = false;
211
212 if (dev_max->pwr_rx == FAST_MODE)
213 is_dev_sup_hs = true;
214
215 if (pltfrm_param->desired_working_mode == UFS_HS_MODE) {
216 is_pltfrm_max_hs = true;
217 min_pltfrm_gear = min_t(u32, pltfrm_param->hs_rx_gear,
218 pltfrm_param->hs_tx_gear);
219 } else {
220 min_pltfrm_gear = min_t(u32, pltfrm_param->pwm_rx_gear,
221 pltfrm_param->pwm_tx_gear);
222 }
223
224 /*
225 * device doesn't support HS but
226 * pltfrm_param->desired_working_mode is HS,
227 * thus device and pltfrm_param don't agree
228 */
229 if (!is_dev_sup_hs && is_pltfrm_max_hs) {
230 pr_info("%s: device doesn't support HS\n",
231 __func__);
232 return -ENOTSUPP;
233 } else if (is_dev_sup_hs && is_pltfrm_max_hs) {
234 /*
235 * since device supports HS, it supports FAST_MODE.
236 * since pltfrm_param->desired_working_mode is also HS
237 * then final decision (FAST/FASTAUTO) is done according
238 * to pltfrm_params as it is the restricting factor
239 */
240 agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_hs;
241 agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
242 } else {
243 /*
244 * here pltfrm_param->desired_working_mode is PWM.
245 * it doesn't matter whether device supports HS or PWM,
246 * in both cases pltfrm_param->desired_working_mode will
247 * determine the mode
248 */
249 agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_pwm;
250 agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
251 }
252
253 /*
254 * we would like tx to work in the minimum number of lanes
255 * between device capability and vendor preferences.
256 * the same decision will be made for rx
257 */
258 agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx,
259 pltfrm_param->tx_lanes);
260 agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx,
261 pltfrm_param->rx_lanes);
262
263 /* device maximum gear is the minimum between device rx and tx gears */
264 min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx);
265
266 /*
267 * if both device capabilities and vendor pre-defined preferences are
268 * both HS or both PWM then set the minimum gear to be the chosen
269 * working gear.
270 * if one is PWM and one is HS then the one that is PWM get to decide
271 * what is the gear, as it is the one that also decided previously what
272 * pwr the device will be configured to.
273 */
274 if ((is_dev_sup_hs && is_pltfrm_max_hs) ||
275 (!is_dev_sup_hs && !is_pltfrm_max_hs)) {
276 agreed_pwr->gear_rx =
277 min_t(u32, min_dev_gear, min_pltfrm_gear);
278 } else if (!is_dev_sup_hs) {
279 agreed_pwr->gear_rx = min_dev_gear;
280 } else {
281 agreed_pwr->gear_rx = min_pltfrm_gear;
282 }
283 agreed_pwr->gear_tx = agreed_pwr->gear_rx;
284
285 agreed_pwr->hs_rate = pltfrm_param->hs_rate;
286
287 return 0;
288}
289EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param);
290
Stanley Chu65858012020-11-16 14:50:47 +0800291void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param)
292{
293 dev_param->tx_lanes = 2;
294 dev_param->rx_lanes = 2;
295 dev_param->hs_rx_gear = UFS_HS_G3;
296 dev_param->hs_tx_gear = UFS_HS_G3;
297 dev_param->pwm_rx_gear = UFS_PWM_G4;
298 dev_param->pwm_tx_gear = UFS_PWM_G4;
299 dev_param->rx_pwr_pwm = SLOW_MODE;
300 dev_param->tx_pwr_pwm = SLOW_MODE;
301 dev_param->rx_pwr_hs = FAST_MODE;
302 dev_param->tx_pwr_hs = FAST_MODE;
303 dev_param->hs_rate = PA_HS_MODE_B;
304 dev_param->desired_working_mode = UFS_HS_MODE;
305}
306EXPORT_SYMBOL_GPL(ufshcd_init_pwr_dev_param);
307
Stanley Chu58b60a92019-03-16 13:04:41 +0800308/**
Yaniv Gardi47555a52015-10-28 13:15:49 +0200309 * ufshcd_pltfrm_init - probe routine of the driver
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530310 * @pdev: pointer to Platform device handle
Yaniv Gardi47555a52015-10-28 13:15:49 +0200311 * @vops: pointer to variant ops
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530312 *
313 * Returns 0 on success, non-zero value on failure
314 */
Yaniv Gardi47555a52015-10-28 13:15:49 +0200315int ufshcd_pltfrm_init(struct platform_device *pdev,
Arnd Bergmann176eb922019-03-04 20:39:11 +0100316 const struct ufs_hba_variant_ops *vops)
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530317{
318 struct ufs_hba *hba;
319 void __iomem *mmio_base;
Seungwon Jeon2953f852013-06-27 13:31:54 +0900320 int irq, err;
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530321 struct device *dev = &pdev->dev;
322
YueHaibing0b275552019-09-04 21:03:48 +0800323 mmio_base = devm_platform_ioremap_resource(pdev, 0);
Tomas Winkler645b8ef2017-03-14 14:19:36 +0200324 if (IS_ERR(mmio_base)) {
325 err = PTR_ERR(mmio_base);
Seungwon Jeon2953f852013-06-27 13:31:54 +0900326 goto out;
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530327 }
328
Seungwon Jeon2953f852013-06-27 13:31:54 +0900329 irq = platform_get_irq(pdev, 0);
330 if (irq < 0) {
Sergey Shtylyov339c9b62021-03-29 23:50:58 +0300331 err = irq;
Seungwon Jeon2953f852013-06-27 13:31:54 +0900332 goto out;
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530333 }
334
Sujit Reddy Thumma5c0c28a2014-09-25 15:32:21 +0300335 err = ufshcd_alloc_host(dev, &hba);
336 if (err) {
337 dev_err(&pdev->dev, "Allocation failed\n");
338 goto out;
339 }
340
Yaniv Gardi47555a52015-10-28 13:15:49 +0200341 hba->vops = vops;
Sujit Reddy Thumma5c0c28a2014-09-25 15:32:21 +0300342
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +0300343 err = ufshcd_parse_clock_info(hba);
344 if (err) {
345 dev_err(&pdev->dev, "%s: clock parse failed %d\n",
346 __func__, err);
Yaniv Gardi47555a52015-10-28 13:15:49 +0200347 goto dealloc_host;
Sujit Reddy Thummac6e79da2014-09-25 15:32:23 +0300348 }
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300349 err = ufshcd_parse_regulator_info(hba);
350 if (err) {
351 dev_err(&pdev->dev, "%s: regulator init failed %d\n",
352 __func__, err);
Yaniv Gardi47555a52015-10-28 13:15:49 +0200353 goto dealloc_host;
Sujit Reddy Thummaaa497612014-09-25 15:32:22 +0300354 }
355
Yaniv Gardi54b879b2016-03-10 17:37:05 +0200356 ufshcd_init_lanes_per_dir(hba);
357
Sujit Reddy Thumma5c0c28a2014-09-25 15:32:21 +0300358 err = ufshcd_init(hba, mmio_base, irq);
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530359 if (err) {
Colin Ian Kingbad97642015-11-28 16:33:56 +0000360 dev_err(dev, "Initialization failed\n");
Stanley Chu24e2e7a12019-06-12 23:19:05 +0800361 goto dealloc_host;
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530362 }
363
364 platform_set_drvdata(pdev, hba);
365
Stanley Chu24e2e7a12019-06-12 23:19:05 +0800366 pm_runtime_set_active(&pdev->dev);
367 pm_runtime_enable(&pdev->dev);
368
Sujit Reddy Thumma62694732013-07-30 00:36:00 +0530369 return 0;
370
Yaniv Gardi47555a52015-10-28 13:15:49 +0200371dealloc_host:
372 ufshcd_dealloc_host(hba);
Seungwon Jeon2953f852013-06-27 13:31:54 +0900373out:
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530374 return err;
375}
Yaniv Gardi47555a52015-10-28 13:15:49 +0200376EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530377
378MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
379MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
Joao Pintocc4959c2016-05-11 12:21:25 +0100380MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
Vinayak Holikatti03b17812013-02-26 18:04:45 +0530381MODULE_LICENSE("GPL");
382MODULE_VERSION(UFSHCD_DRIVER_VERSION);