ufshcd-pltfrm.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /*
  2. * Universal Flash Storage Host controller Platform bus based glue driver
  3. *
  4. * This code is based on drivers/scsi/ufs/ufshcd-pltfrm.c
  5. * Copyright (C) 2011-2013 Samsung India Software Operations
  6. *
  7. * Authors:
  8. * Santosh Yaraganavi <santosh.sy@samsung.com>
  9. * Vinayak Holikatti <h.vinayak@samsung.com>
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. * See the COPYING file in the top-level directory or visit
  16. * <http://www.gnu.org/licenses/gpl-2.0.html>
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * This program is provided "AS IS" and "WITH ALL FAULTS" and
  24. * without warranty of any kind. You are solely responsible for
  25. * determining the appropriateness of using and distributing
  26. * the program and assume all risks associated with your exercise
  27. * of rights with respect to the program, including but not limited
  28. * to infringement of third party rights, the risks and costs of
  29. * program errors, damage to or loss of data, programs or equipment,
  30. * and unavailability or interruption of operations. Under no
  31. * circumstances will the contributor of this Program be liable for
  32. * any damages of any kind arising from your use or distribution of
  33. * this program.
  34. */
  35. #include <linux/platform_device.h>
  36. #include <linux/pm_runtime.h>
  37. #include <linux/of.h>
  38. #include "ufshcd.h"
  39. #include "ufshcd-pltfrm.h"
  40. #define UFSHCD_DEFAULT_LANES_PER_DIRECTION 2
  41. static int ufshcd_parse_clock_info(struct ufs_hba *hba)
  42. {
  43. int ret = 0;
  44. int cnt;
  45. int i;
  46. struct device *dev = hba->dev;
  47. struct device_node *np = dev->of_node;
  48. char *name;
  49. u32 *clkfreq = NULL;
  50. struct ufs_clk_info *clki;
  51. int len = 0;
  52. size_t sz = 0;
  53. if (!np)
  54. goto out;
  55. INIT_LIST_HEAD(&hba->clk_list_head);
  56. cnt = of_property_count_strings(np, "clock-names");
  57. if (!cnt || (cnt == -EINVAL)) {
  58. dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
  59. __func__);
  60. } else if (cnt < 0) {
  61. dev_err(dev, "%s: count clock strings failed, err %d\n",
  62. __func__, cnt);
  63. ret = cnt;
  64. }
  65. if (cnt <= 0)
  66. goto out;
  67. if (!of_get_property(np, "freq-table-hz", &len)) {
  68. dev_info(dev, "freq-table-hz property not specified\n");
  69. goto out;
  70. }
  71. if (len <= 0)
  72. goto out;
  73. sz = len / sizeof(*clkfreq);
  74. if (sz != 2 * cnt) {
  75. dev_err(dev, "%s len mismatch\n", "freq-table-hz");
  76. ret = -EINVAL;
  77. goto out;
  78. }
  79. clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq),
  80. GFP_KERNEL);
  81. if (!clkfreq) {
  82. ret = -ENOMEM;
  83. goto out;
  84. }
  85. ret = of_property_read_u32_array(np, "freq-table-hz",
  86. clkfreq, sz);
  87. if (ret && (ret != -EINVAL)) {
  88. dev_err(dev, "%s: error reading array %d\n",
  89. "freq-table-hz", ret);
  90. return ret;
  91. }
  92. for (i = 0; i < sz; i += 2) {
  93. ret = of_property_read_string_index(np,
  94. "clock-names", i/2, (const char **)&name);
  95. if (ret)
  96. goto out;
  97. clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
  98. if (!clki) {
  99. ret = -ENOMEM;
  100. goto out;
  101. }
  102. clki->min_freq = clkfreq[i];
  103. clki->max_freq = clkfreq[i+1];
  104. clki->name = kstrdup(name, GFP_KERNEL);
  105. dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz",
  106. clki->min_freq, clki->max_freq, clki->name);
  107. list_add_tail(&clki->list, &hba->clk_list_head);
  108. }
  109. out:
  110. return ret;
  111. }
  112. #define MAX_PROP_SIZE 32
  113. static int ufshcd_populate_vreg(struct device *dev, const char *name,
  114. struct ufs_vreg **out_vreg)
  115. {
  116. int ret = 0;
  117. char prop_name[MAX_PROP_SIZE];
  118. struct ufs_vreg *vreg = NULL;
  119. struct device_node *np = dev->of_node;
  120. if (!np) {
  121. dev_err(dev, "%s: non DT initialization\n", __func__);
  122. goto out;
  123. }
  124. snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
  125. if (!of_parse_phandle(np, prop_name, 0)) {
  126. dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
  127. __func__, prop_name);
  128. goto out;
  129. }
  130. vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
  131. if (!vreg)
  132. return -ENOMEM;
  133. vreg->name = kstrdup(name, GFP_KERNEL);
  134. /* if fixed regulator no need further initialization */
  135. snprintf(prop_name, MAX_PROP_SIZE, "%s-fixed-regulator", name);
  136. if (of_property_read_bool(np, prop_name))
  137. goto out;
  138. snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
  139. ret = of_property_read_u32(np, prop_name, &vreg->max_uA);
  140. if (ret) {
  141. dev_err(dev, "%s: unable to find %s err %d\n",
  142. __func__, prop_name, ret);
  143. goto out_free;
  144. }
  145. vreg->min_uA = 0;
  146. if (!strcmp(name, "vcc")) {
  147. if (of_property_read_bool(np, "vcc-supply-1p8")) {
  148. vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV;
  149. vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV;
  150. } else {
  151. vreg->min_uV = UFS_VREG_VCC_MIN_UV;
  152. vreg->max_uV = UFS_VREG_VCC_MAX_UV;
  153. }
  154. } else if (!strcmp(name, "vccq")) {
  155. vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
  156. vreg->max_uV = UFS_VREG_VCCQ_MAX_UV;
  157. } else if (!strcmp(name, "vccq2")) {
  158. vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV;
  159. vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV;
  160. }
  161. goto out;
  162. out_free:
  163. devm_kfree(dev, vreg);
  164. vreg = NULL;
  165. out:
  166. if (!ret)
  167. *out_vreg = vreg;
  168. return ret;
  169. }
  170. /**
  171. * ufshcd_parse_regulator_info - get regulator info from device tree
  172. * @hba: per adapter instance
  173. *
  174. * Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
  175. * If any of the supplies are not defined it is assumed that they are always-on
  176. * and hence return zero. If the property is defined but parsing is failed
  177. * then return corresponding error.
  178. */
  179. static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
  180. {
  181. int err;
  182. struct device *dev = hba->dev;
  183. struct ufs_vreg_info *info = &hba->vreg_info;
  184. err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba);
  185. if (err)
  186. goto out;
  187. err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
  188. if (err)
  189. goto out;
  190. err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
  191. if (err)
  192. goto out;
  193. err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
  194. out:
  195. return err;
  196. }
  197. #ifdef CONFIG_PM
  198. /**
  199. * ufshcd_pltfrm_suspend - suspend power management function
  200. * @dev: pointer to device handle
  201. *
  202. * Returns 0 if successful
  203. * Returns non-zero otherwise
  204. */
  205. int ufshcd_pltfrm_suspend(struct device *dev)
  206. {
  207. return ufshcd_system_suspend(dev_get_drvdata(dev));
  208. }
  209. EXPORT_SYMBOL_GPL(ufshcd_pltfrm_suspend);
  210. /**
  211. * ufshcd_pltfrm_resume - resume power management function
  212. * @dev: pointer to device handle
  213. *
  214. * Returns 0 if successful
  215. * Returns non-zero otherwise
  216. */
  217. int ufshcd_pltfrm_resume(struct device *dev)
  218. {
  219. return ufshcd_system_resume(dev_get_drvdata(dev));
  220. }
  221. EXPORT_SYMBOL_GPL(ufshcd_pltfrm_resume);
  222. int ufshcd_pltfrm_runtime_suspend(struct device *dev)
  223. {
  224. return ufshcd_runtime_suspend(dev_get_drvdata(dev));
  225. }
  226. EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_suspend);
  227. int ufshcd_pltfrm_runtime_resume(struct device *dev)
  228. {
  229. return ufshcd_runtime_resume(dev_get_drvdata(dev));
  230. }
  231. EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_resume);
  232. int ufshcd_pltfrm_runtime_idle(struct device *dev)
  233. {
  234. return ufshcd_runtime_idle(dev_get_drvdata(dev));
  235. }
  236. EXPORT_SYMBOL_GPL(ufshcd_pltfrm_runtime_idle);
  237. #endif /* CONFIG_PM */
  238. void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
  239. {
  240. ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
  241. }
  242. EXPORT_SYMBOL_GPL(ufshcd_pltfrm_shutdown);
  243. static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
  244. {
  245. struct device *dev = hba->dev;
  246. int ret;
  247. ret = of_property_read_u32(dev->of_node, "lanes-per-direction",
  248. &hba->lanes_per_direction);
  249. if (ret) {
  250. dev_dbg(hba->dev,
  251. "%s: failed to read lanes-per-direction, ret=%d\n",
  252. __func__, ret);
  253. hba->lanes_per_direction = UFSHCD_DEFAULT_LANES_PER_DIRECTION;
  254. }
  255. }
  256. /**
  257. * ufshcd_pltfrm_init - probe routine of the driver
  258. * @pdev: pointer to Platform device handle
  259. * @vops: pointer to variant ops
  260. *
  261. * Returns 0 on success, non-zero value on failure
  262. */
  263. int ufshcd_pltfrm_init(struct platform_device *pdev,
  264. struct ufs_hba_variant_ops *vops)
  265. {
  266. struct ufs_hba *hba;
  267. void __iomem *mmio_base;
  268. struct resource *mem_res;
  269. int irq, err;
  270. struct device *dev = &pdev->dev;
  271. mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  272. mmio_base = devm_ioremap_resource(dev, mem_res);
  273. if (IS_ERR(*(void **)&mmio_base)) {
  274. err = PTR_ERR(*(void **)&mmio_base);
  275. goto out;
  276. }
  277. irq = platform_get_irq(pdev, 0);
  278. if (irq < 0) {
  279. dev_err(dev, "IRQ resource not available\n");
  280. err = -ENODEV;
  281. goto out;
  282. }
  283. err = ufshcd_alloc_host(dev, &hba);
  284. if (err) {
  285. dev_err(&pdev->dev, "Allocation failed\n");
  286. goto out;
  287. }
  288. hba->vops = vops;
  289. err = ufshcd_parse_clock_info(hba);
  290. if (err) {
  291. dev_err(&pdev->dev, "%s: clock parse failed %d\n",
  292. __func__, err);
  293. goto dealloc_host;
  294. }
  295. err = ufshcd_parse_regulator_info(hba);
  296. if (err) {
  297. dev_err(&pdev->dev, "%s: regulator init failed %d\n",
  298. __func__, err);
  299. goto dealloc_host;
  300. }
  301. pm_runtime_set_active(&pdev->dev);
  302. pm_runtime_enable(&pdev->dev);
  303. ufshcd_init_lanes_per_dir(hba);
  304. err = ufshcd_init(hba, mmio_base, irq);
  305. if (err) {
  306. dev_err(dev, "Initialization failed\n");
  307. goto out_disable_rpm;
  308. }
  309. platform_set_drvdata(pdev, hba);
  310. return 0;
  311. out_disable_rpm:
  312. pm_runtime_disable(&pdev->dev);
  313. pm_runtime_set_suspended(&pdev->dev);
  314. dealloc_host:
  315. ufshcd_dealloc_host(hba);
  316. out:
  317. return err;
  318. }
  319. EXPORT_SYMBOL_GPL(ufshcd_pltfrm_init);
  320. MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
  321. MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
  322. MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
  323. MODULE_LICENSE("GPL");
  324. MODULE_VERSION(UFSHCD_DRIVER_VERSION);