Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Universal Flash Storage Host controller Platform bus based glue driver
4 * 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>
9 */
10
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/pm_opp.h>
14#include <linux/pm_runtime.h>
15#include <linux/of.h>
16
17#include <ufs/ufshcd.h>
18#include "ufshcd-pltfrm.h"
19#include <ufs/unipro.h>
20
21#define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
22
23static int ufshcd_parse_clock_info(struct ufs_hba *hba)
24{
25 int ret = 0;
26 int cnt;
27 int i;
28 struct device *dev = hba->dev;
29 struct device_node *np = dev->of_node;
30 const char *name;
31 u32 *clkfreq = NULL;
32 struct ufs_clk_info *clki;
33 int len = 0;
34 size_t sz = 0;
35
36 if (!np)
37 goto out;
38
39 cnt = of_property_count_strings(np, "clock-names");
40 if (!cnt || (cnt == -EINVAL)) {
41 dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
42 __func__);
43 } else if (cnt < 0) {
44 dev_err(dev, "%s: count clock strings failed, err %d\n",
45 __func__, cnt);
46 ret = cnt;
47 }
48
49 if (cnt <= 0)
50 goto out;
51
52 if (!of_get_property(np, "freq-table-hz", &len)) {
53 dev_info(dev, "freq-table-hz property not specified\n");
54 goto out;
55 }
56
57 if (len <= 0)
58 goto out;
59
60 sz = len / sizeof(*clkfreq);
61 if (sz != 2 * cnt) {
62 dev_err(dev, "%s len mismatch\n", "freq-table-hz");
63 ret = -EINVAL;
64 goto out;
65 }
66
67 clkfreq = devm_kcalloc(dev, sz, sizeof(*clkfreq),
68 GFP_KERNEL);
69 if (!clkfreq) {
70 ret = -ENOMEM;
71 goto out;
72 }
73
74 ret = of_property_read_u32_array(np, "freq-table-hz",
75 clkfreq, sz);
76 if (ret && (ret != -EINVAL)) {
77 dev_err(dev, "%s: error reading array %d\n",
78 "freq-table-hz", ret);
79 return ret;
80 }
81
82 for (i = 0; i < sz; i += 2) {
83 ret = of_property_read_string_index(np, "clock-names", i/2,
84 &name);
85 if (ret)
86 goto out;
87
88 clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
89 if (!clki) {
90 ret = -ENOMEM;
91 goto out;
92 }
93
94 clki->min_freq = clkfreq[i];
95 clki->max_freq = clkfreq[i+1];
96 clki->name = devm_kstrdup(dev, name, GFP_KERNEL);
97 if (!clki->name) {
98 ret = -ENOMEM;
99 goto out;
100 }
101
102 if (!strcmp(name, "ref_clk"))
103 clki->keep_link_active = true;
104 dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz",
105 clki->min_freq, clki->max_freq, clki->name);
106 list_add_tail(&clki->list, &hba->clk_list_head);
107 }
108out:
109 return ret;
110}
111
112static bool phandle_exists(const struct device_node *np,
113 const char *phandle_name, int index)
114{
115 struct device_node *parse_np = of_parse_phandle(np, phandle_name, index);
116
117 if (parse_np)
118 of_node_put(parse_np);
119
120 return parse_np != NULL;
121}
122
123#define MAX_PROP_SIZE 32
124int ufshcd_populate_vreg(struct device *dev, const char *name,
125 struct ufs_vreg **out_vreg, bool skip_current)
126{
127 char prop_name[MAX_PROP_SIZE];
128 struct ufs_vreg *vreg = NULL;
129 struct device_node *np = dev->of_node;
130
131 if (!np) {
132 dev_err(dev, "%s: non DT initialization\n", __func__);
133 goto out;
134 }
135
136 snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
137 if (!phandle_exists(np, prop_name, 0)) {
138 dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
139 __func__, prop_name);
140 goto out;
141 }
142
143 vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
144 if (!vreg)
145 return -ENOMEM;
146
147 vreg->name = devm_kstrdup(dev, name, GFP_KERNEL);
148 if (!vreg->name)
149 return -ENOMEM;
150
151 if (skip_current) {
152 vreg->max_uA = 0;
153 goto out;
154 }
155
156 snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
157 if (of_property_read_u32(np, prop_name, &vreg->max_uA)) {
158 dev_info(dev, "%s: unable to find %s\n", __func__, prop_name);
159 vreg->max_uA = 0;
160 }
161out:
162 *out_vreg = vreg;
163 return 0;
164}
165EXPORT_SYMBOL_GPL(ufshcd_populate_vreg);
166
167/**
168 * ufshcd_parse_regulator_info - get regulator info from device tree
169 * @hba: per adapter instance
170 *
171 * Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
172 * If any of the supplies are not defined it is assumed that they are always-on
173 * and hence return zero. If the property is defined but parsing is failed
174 * then return corresponding error.
175 *
176 * Return: 0 upon success; < 0 upon failure.
177 */
178static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
179{
180 int err;
181 struct device *dev = hba->dev;
182 struct ufs_vreg_info *info = &hba->vreg_info;
183
184 err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba, true);
185 if (err)
186 goto out;
187
188 err = ufshcd_populate_vreg(dev, "vcc", &info->vcc, false);
189 if (err)
190 goto out;
191
192 err = ufshcd_populate_vreg(dev, "vccq", &info->vccq, false);
193 if (err)
194 goto out;
195
196 err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2, false);
197out:
198 return err;
199}
200
201static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
202{
203 struct device *dev = hba->dev;
204 int ret;
205
206 ret = of_property_read_u32(dev->of_node, "lanes-per-direction",
207 &hba->lanes_per_direction);
208 if (ret) {
209 dev_dbg(hba->dev,
210 "%s: failed to read lanes-per-direction, ret=%d\n",
211 __func__, ret);
212 hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION;
213 }
214}
215
216static int ufshcd_parse_operating_points(struct ufs_hba *hba)
217{
218 struct device *dev = hba->dev;
219 struct device_node *np = dev->of_node;
220 struct dev_pm_opp_config config = {};
221 struct ufs_clk_info *clki;
222 const char **clk_names;
223 int cnt, i, ret;
224
225 if (!of_find_property(np, "operating-points-v2", NULL))
226 return 0;
227
228 if (of_find_property(np, "freq-table-hz", NULL)) {
229 dev_err(dev, "%s: operating-points and freq-table-hz are incompatible\n",
230 __func__);
231 return -EINVAL;
232 }
233
234 cnt = of_property_count_strings(np, "clock-names");
235 if (cnt <= 0) {
236 dev_err(dev, "%s: Missing clock-names\n", __func__);
237 return -ENODEV;
238 }
239
240 /* OPP expects clk_names to be NULL terminated */
241 clk_names = devm_kcalloc(dev, cnt + 1, sizeof(*clk_names), GFP_KERNEL);
242 if (!clk_names)
243 return -ENOMEM;
244
245 /*
246 * We still need to get reference to all clocks as the UFS core uses
247 * them separately.
248 */
249 for (i = 0; i < cnt; i++) {
250 ret = of_property_read_string_index(np, "clock-names", i,
251 &clk_names[i]);
252 if (ret)
253 return ret;
254
255 clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
256 if (!clki)
257 return -ENOMEM;
258
259 clki->name = devm_kstrdup(dev, clk_names[i], GFP_KERNEL);
260 if (!clki->name)
261 return -ENOMEM;
262
263 if (!strcmp(clk_names[i], "ref_clk"))
264 clki->keep_link_active = true;
265
266 list_add_tail(&clki->list, &hba->clk_list_head);
267 }
268
269 config.clk_names = clk_names,
270 config.config_clks = ufshcd_opp_config_clks;
271
272 ret = devm_pm_opp_set_config(dev, &config);
273 if (ret)
274 return ret;
275
276 ret = devm_pm_opp_of_add_table(dev);
277 if (ret) {
278 dev_err(dev, "Failed to add OPP table: %d\n", ret);
279 return ret;
280 }
281
282 hba->use_pm_opp = true;
283
284 return 0;
285}
286
287/**
288 * ufshcd_get_pwr_dev_param - get finally agreed attributes for
289 * power mode change
290 * @pltfrm_param: pointer to platform parameters
291 * @dev_max: pointer to device attributes
292 * @agreed_pwr: returned agreed attributes
293 *
294 * Return: 0 on success, non-zero value on failure.
295 */
296int ufshcd_get_pwr_dev_param(const struct ufs_dev_params *pltfrm_param,
297 const struct ufs_pa_layer_attr *dev_max,
298 struct ufs_pa_layer_attr *agreed_pwr)
299{
300 int min_pltfrm_gear;
301 int min_dev_gear;
302 bool is_dev_sup_hs = false;
303 bool is_pltfrm_max_hs = false;
304
305 if (dev_max->pwr_rx == FAST_MODE)
306 is_dev_sup_hs = true;
307
308 if (pltfrm_param->desired_working_mode == UFS_HS_MODE) {
309 is_pltfrm_max_hs = true;
310 min_pltfrm_gear = min_t(u32, pltfrm_param->hs_rx_gear,
311 pltfrm_param->hs_tx_gear);
312 } else {
313 min_pltfrm_gear = min_t(u32, pltfrm_param->pwm_rx_gear,
314 pltfrm_param->pwm_tx_gear);
315 }
316
317 /*
318 * device doesn't support HS but
319 * pltfrm_param->desired_working_mode is HS,
320 * thus device and pltfrm_param don't agree
321 */
322 if (!is_dev_sup_hs && is_pltfrm_max_hs) {
323 pr_info("%s: device doesn't support HS\n",
324 __func__);
325 return -ENOTSUPP;
326 } else if (is_dev_sup_hs && is_pltfrm_max_hs) {
327 /*
328 * since device supports HS, it supports FAST_MODE.
329 * since pltfrm_param->desired_working_mode is also HS
330 * then final decision (FAST/FASTAUTO) is done according
331 * to pltfrm_params as it is the restricting factor
332 */
333 agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_hs;
334 agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
335 } else {
336 /*
337 * here pltfrm_param->desired_working_mode is PWM.
338 * it doesn't matter whether device supports HS or PWM,
339 * in both cases pltfrm_param->desired_working_mode will
340 * determine the mode
341 */
342 agreed_pwr->pwr_rx = pltfrm_param->rx_pwr_pwm;
343 agreed_pwr->pwr_tx = agreed_pwr->pwr_rx;
344 }
345
346 /*
347 * we would like tx to work in the minimum number of lanes
348 * between device capability and vendor preferences.
349 * the same decision will be made for rx
350 */
351 agreed_pwr->lane_tx = min_t(u32, dev_max->lane_tx,
352 pltfrm_param->tx_lanes);
353 agreed_pwr->lane_rx = min_t(u32, dev_max->lane_rx,
354 pltfrm_param->rx_lanes);
355
356 /* device maximum gear is the minimum between device rx and tx gears */
357 min_dev_gear = min_t(u32, dev_max->gear_rx, dev_max->gear_tx);
358
359 /*
360 * if both device capabilities and vendor pre-defined preferences are
361 * both HS or both PWM then set the minimum gear to be the chosen
362 * working gear.
363 * if one is PWM and one is HS then the one that is PWM get to decide
364 * what is the gear, as it is the one that also decided previously what
365 * pwr the device will be configured to.
366 */
367 if ((is_dev_sup_hs && is_pltfrm_max_hs) ||
368 (!is_dev_sup_hs && !is_pltfrm_max_hs)) {
369 agreed_pwr->gear_rx =
370 min_t(u32, min_dev_gear, min_pltfrm_gear);
371 } else if (!is_dev_sup_hs) {
372 agreed_pwr->gear_rx = min_dev_gear;
373 } else {
374 agreed_pwr->gear_rx = min_pltfrm_gear;
375 }
376 agreed_pwr->gear_tx = agreed_pwr->gear_rx;
377
378 agreed_pwr->hs_rate = pltfrm_param->hs_rate;
379
380 return 0;
381}
382EXPORT_SYMBOL_GPL(ufshcd_get_pwr_dev_param);
383
384void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param)
385{
386 *dev_param = (struct ufs_dev_params){
387 .tx_lanes = UFS_LANE_2,
388 .rx_lanes = UFS_LANE_2,
389 .hs_rx_gear = UFS_HS_G3,
390 .hs_tx_gear = UFS_HS_G3,
391 .pwm_rx_gear = UFS_PWM_G4,
392 .pwm_tx_gear = UFS_PWM_G4,
393 .rx_pwr_pwm = SLOW_MODE,
394 .tx_pwr_pwm = SLOW_MODE,
395 .rx_pwr_hs = FAST_MODE,
396 .tx_pwr_hs = FAST_MODE,
397 .hs_rate = PA_HS_MODE_B,
398 .desired_working_mode = UFS_HS_MODE,
399 };
400}
401EXPORT_SYMBOL_GPL(ufshcd_init_pwr_dev_param);
402
403/**
404 * ufshcd_pltfrm_init - probe routine of the driver
405 * @pdev: pointer to Platform device handle
406 * @vops: pointer to variant ops
407 *
408 * Return: 0 on success, non-zero value on failure.
409 */
410int ufshcd_pltfrm_init(struct platform_device *pdev,
411 const struct ufs_hba_variant_ops *vops)
412{
413 struct ufs_hba *hba;
414 void __iomem *mmio_base;
415 int irq, err;
416 struct device *dev = &pdev->dev;
417
418 mmio_base = devm_platform_ioremap_resource(pdev, 0);
419 if (IS_ERR(mmio_base)) {
420 err = PTR_ERR(mmio_base);
421 goto out;
422 }
423
424 irq = platform_get_irq(pdev, 0);
425 if (irq < 0) {
426 err = irq;
427 goto out;
428 }
429
430 err = ufshcd_alloc_host(dev, &hba);
431 if (err) {
432 dev_err(dev, "Allocation failed\n");
433 goto out;
434 }
435
436 hba->vops = vops;
437
438 err = ufshcd_parse_clock_info(hba);
439 if (err) {
440 dev_err(dev, "%s: clock parse failed %d\n",
441 __func__, err);
442 goto dealloc_host;
443 }
444 err = ufshcd_parse_regulator_info(hba);
445 if (err) {
446 dev_err(dev, "%s: regulator init failed %d\n",
447 __func__, err);
448 goto dealloc_host;
449 }
450
451 ufshcd_init_lanes_per_dir(hba);
452
453 err = ufshcd_parse_operating_points(hba);
454 if (err) {
455 dev_err(dev, "%s: OPP parse failed %d\n", __func__, err);
456 goto dealloc_host;
457 }
458
459 err = ufshcd_init(hba, mmio_base, irq);
460 if (err) {
461 dev_err_probe(dev, err, "Initialization failed with error %d\n",
462 err);
463 goto dealloc_host;
464 }
465
466 pm_runtime_set_active(dev);
467 pm_runtime_enable(dev);
468
469 return 0;
470
471dealloc_host:
472 ufshcd_dealloc_host(hba);
473out:
474 return err;
475}
476EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
477
478MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
479MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
480MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
481MODULE_LICENSE("GPL");