ufshcd-pltfrm.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  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. static const struct of_device_id ufs_of_match[];
  40. static struct ufs_hba_variant_ops *get_variant_ops(struct device *dev)
  41. {
  42. if (dev->of_node) {
  43. const struct of_device_id *match;
  44. match = of_match_node(ufs_of_match, dev->of_node);
  45. if (match)
  46. return (struct ufs_hba_variant_ops *)match->data;
  47. }
  48. return NULL;
  49. }
  50. static int ufshcd_parse_clock_info(struct ufs_hba *hba)
  51. {
  52. int ret = 0;
  53. int cnt;
  54. int i;
  55. struct device *dev = hba->dev;
  56. struct device_node *np = dev->of_node;
  57. char *name;
  58. u32 *clkfreq = NULL;
  59. struct ufs_clk_info *clki;
  60. int len = 0;
  61. size_t sz = 0;
  62. if (!np)
  63. goto out;
  64. INIT_LIST_HEAD(&hba->clk_list_head);
  65. cnt = of_property_count_strings(np, "clock-names");
  66. if (!cnt || (cnt == -EINVAL)) {
  67. dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
  68. __func__);
  69. } else if (cnt < 0) {
  70. dev_err(dev, "%s: count clock strings failed, err %d\n",
  71. __func__, cnt);
  72. ret = cnt;
  73. }
  74. if (cnt <= 0)
  75. goto out;
  76. if (!of_get_property(np, "freq-table-hz", &len)) {
  77. dev_info(dev, "freq-table-hz property not specified\n");
  78. goto out;
  79. }
  80. if (len <= 0)
  81. goto out;
  82. sz = len / sizeof(*clkfreq);
  83. if (sz != 2 * cnt) {
  84. dev_err(dev, "%s len mismatch\n", "freq-table-hz");
  85. ret = -EINVAL;
  86. goto out;
  87. }
  88. clkfreq = devm_kzalloc(dev, sz * sizeof(*clkfreq),
  89. GFP_KERNEL);
  90. if (!clkfreq) {
  91. ret = -ENOMEM;
  92. goto out;
  93. }
  94. ret = of_property_read_u32_array(np, "freq-table-hz",
  95. clkfreq, sz);
  96. if (ret && (ret != -EINVAL)) {
  97. dev_err(dev, "%s: error reading array %d\n",
  98. "freq-table-hz", ret);
  99. return ret;
  100. }
  101. for (i = 0; i < sz; i += 2) {
  102. ret = of_property_read_string_index(np,
  103. "clock-names", i/2, (const char **)&name);
  104. if (ret)
  105. goto out;
  106. clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
  107. if (!clki) {
  108. ret = -ENOMEM;
  109. goto out;
  110. }
  111. clki->min_freq = clkfreq[i];
  112. clki->max_freq = clkfreq[i+1];
  113. clki->name = kstrdup(name, GFP_KERNEL);
  114. dev_dbg(dev, "%s: min %u max %u name %s\n", "freq-table-hz",
  115. clki->min_freq, clki->max_freq, clki->name);
  116. list_add_tail(&clki->list, &hba->clk_list_head);
  117. }
  118. out:
  119. return ret;
  120. }
  121. #define MAX_PROP_SIZE 32
  122. static int ufshcd_populate_vreg(struct device *dev, const char *name,
  123. struct ufs_vreg **out_vreg)
  124. {
  125. int ret = 0;
  126. char prop_name[MAX_PROP_SIZE];
  127. struct ufs_vreg *vreg = NULL;
  128. struct device_node *np = dev->of_node;
  129. if (!np) {
  130. dev_err(dev, "%s: non DT initialization\n", __func__);
  131. goto out;
  132. }
  133. snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", name);
  134. if (!of_parse_phandle(np, prop_name, 0)) {
  135. dev_info(dev, "%s: Unable to find %s regulator, assuming enabled\n",
  136. __func__, prop_name);
  137. goto out;
  138. }
  139. vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
  140. if (!vreg)
  141. return -ENOMEM;
  142. vreg->name = kstrdup(name, GFP_KERNEL);
  143. /* if fixed regulator no need further initialization */
  144. snprintf(prop_name, MAX_PROP_SIZE, "%s-fixed-regulator", name);
  145. if (of_property_read_bool(np, prop_name))
  146. goto out;
  147. snprintf(prop_name, MAX_PROP_SIZE, "%s-max-microamp", name);
  148. ret = of_property_read_u32(np, prop_name, &vreg->max_uA);
  149. if (ret) {
  150. dev_err(dev, "%s: unable to find %s err %d\n",
  151. __func__, prop_name, ret);
  152. goto out_free;
  153. }
  154. vreg->min_uA = 0;
  155. if (!strcmp(name, "vcc")) {
  156. if (of_property_read_bool(np, "vcc-supply-1p8")) {
  157. vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV;
  158. vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV;
  159. } else {
  160. vreg->min_uV = UFS_VREG_VCC_MIN_UV;
  161. vreg->max_uV = UFS_VREG_VCC_MAX_UV;
  162. }
  163. } else if (!strcmp(name, "vccq")) {
  164. vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
  165. vreg->max_uV = UFS_VREG_VCCQ_MAX_UV;
  166. } else if (!strcmp(name, "vccq2")) {
  167. vreg->min_uV = UFS_VREG_VCCQ2_MIN_UV;
  168. vreg->max_uV = UFS_VREG_VCCQ2_MAX_UV;
  169. }
  170. goto out;
  171. out_free:
  172. devm_kfree(dev, vreg);
  173. vreg = NULL;
  174. out:
  175. if (!ret)
  176. *out_vreg = vreg;
  177. return ret;
  178. }
  179. /**
  180. * ufshcd_parse_regulator_info - get regulator info from device tree
  181. * @hba: per adapter instance
  182. *
  183. * Get regulator info from device tree for vcc, vccq, vccq2 power supplies.
  184. * If any of the supplies are not defined it is assumed that they are always-on
  185. * and hence return zero. If the property is defined but parsing is failed
  186. * then return corresponding error.
  187. */
  188. static int ufshcd_parse_regulator_info(struct ufs_hba *hba)
  189. {
  190. int err;
  191. struct device *dev = hba->dev;
  192. struct ufs_vreg_info *info = &hba->vreg_info;
  193. err = ufshcd_populate_vreg(dev, "vdd-hba", &info->vdd_hba);
  194. if (err)
  195. goto out;
  196. err = ufshcd_populate_vreg(dev, "vcc", &info->vcc);
  197. if (err)
  198. goto out;
  199. err = ufshcd_populate_vreg(dev, "vccq", &info->vccq);
  200. if (err)
  201. goto out;
  202. err = ufshcd_populate_vreg(dev, "vccq2", &info->vccq2);
  203. out:
  204. return err;
  205. }
  206. #ifdef CONFIG_PM
  207. /**
  208. * ufshcd_pltfrm_suspend - suspend power management function
  209. * @dev: pointer to device handle
  210. *
  211. * Returns 0 if successful
  212. * Returns non-zero otherwise
  213. */
  214. static int ufshcd_pltfrm_suspend(struct device *dev)
  215. {
  216. return ufshcd_system_suspend(dev_get_drvdata(dev));
  217. }
  218. /**
  219. * ufshcd_pltfrm_resume - resume power management function
  220. * @dev: pointer to device handle
  221. *
  222. * Returns 0 if successful
  223. * Returns non-zero otherwise
  224. */
  225. static int ufshcd_pltfrm_resume(struct device *dev)
  226. {
  227. return ufshcd_system_resume(dev_get_drvdata(dev));
  228. }
  229. #else
  230. #define ufshcd_pltfrm_suspend NULL
  231. #define ufshcd_pltfrm_resume NULL
  232. #endif
  233. #ifdef CONFIG_PM_RUNTIME
  234. static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
  235. {
  236. return ufshcd_runtime_suspend(dev_get_drvdata(dev));
  237. }
  238. static int ufshcd_pltfrm_runtime_resume(struct device *dev)
  239. {
  240. return ufshcd_runtime_resume(dev_get_drvdata(dev));
  241. }
  242. static int ufshcd_pltfrm_runtime_idle(struct device *dev)
  243. {
  244. return ufshcd_runtime_idle(dev_get_drvdata(dev));
  245. }
  246. #else /* !CONFIG_PM_RUNTIME */
  247. #define ufshcd_pltfrm_runtime_suspend NULL
  248. #define ufshcd_pltfrm_runtime_resume NULL
  249. #define ufshcd_pltfrm_runtime_idle NULL
  250. #endif /* CONFIG_PM_RUNTIME */
  251. static void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
  252. {
  253. ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
  254. }
  255. /**
  256. * ufshcd_pltfrm_probe - probe routine of the driver
  257. * @pdev: pointer to Platform device handle
  258. *
  259. * Returns 0 on success, non-zero value on failure
  260. */
  261. static int ufshcd_pltfrm_probe(struct platform_device *pdev)
  262. {
  263. struct ufs_hba *hba;
  264. void __iomem *mmio_base;
  265. struct resource *mem_res;
  266. int irq, err;
  267. struct device *dev = &pdev->dev;
  268. mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  269. mmio_base = devm_ioremap_resource(dev, mem_res);
  270. if (IS_ERR(*(void **)&mmio_base)) {
  271. err = PTR_ERR(*(void **)&mmio_base);
  272. goto out;
  273. }
  274. irq = platform_get_irq(pdev, 0);
  275. if (irq < 0) {
  276. dev_err(dev, "IRQ resource not available\n");
  277. err = -ENODEV;
  278. goto out;
  279. }
  280. err = ufshcd_alloc_host(dev, &hba);
  281. if (err) {
  282. dev_err(&pdev->dev, "Allocation failed\n");
  283. goto out;
  284. }
  285. hba->vops = get_variant_ops(&pdev->dev);
  286. err = ufshcd_parse_clock_info(hba);
  287. if (err) {
  288. dev_err(&pdev->dev, "%s: clock parse failed %d\n",
  289. __func__, err);
  290. goto out;
  291. }
  292. err = ufshcd_parse_regulator_info(hba);
  293. if (err) {
  294. dev_err(&pdev->dev, "%s: regulator init failed %d\n",
  295. __func__, err);
  296. goto out;
  297. }
  298. pm_runtime_set_active(&pdev->dev);
  299. pm_runtime_enable(&pdev->dev);
  300. err = ufshcd_init(hba, mmio_base, irq);
  301. if (err) {
  302. dev_err(dev, "Intialization failed\n");
  303. goto out_disable_rpm;
  304. }
  305. platform_set_drvdata(pdev, hba);
  306. return 0;
  307. out_disable_rpm:
  308. pm_runtime_disable(&pdev->dev);
  309. pm_runtime_set_suspended(&pdev->dev);
  310. out:
  311. return err;
  312. }
  313. /**
  314. * ufshcd_pltfrm_remove - remove platform driver routine
  315. * @pdev: pointer to platform device handle
  316. *
  317. * Returns 0 on success, non-zero value on failure
  318. */
  319. static int ufshcd_pltfrm_remove(struct platform_device *pdev)
  320. {
  321. struct ufs_hba *hba = platform_get_drvdata(pdev);
  322. pm_runtime_get_sync(&(pdev)->dev);
  323. ufshcd_remove(hba);
  324. return 0;
  325. }
  326. static const struct of_device_id ufs_of_match[] = {
  327. { .compatible = "jedec,ufs-1.1"},
  328. {},
  329. };
  330. static const struct dev_pm_ops ufshcd_dev_pm_ops = {
  331. .suspend = ufshcd_pltfrm_suspend,
  332. .resume = ufshcd_pltfrm_resume,
  333. .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
  334. .runtime_resume = ufshcd_pltfrm_runtime_resume,
  335. .runtime_idle = ufshcd_pltfrm_runtime_idle,
  336. };
  337. static struct platform_driver ufshcd_pltfrm_driver = {
  338. .probe = ufshcd_pltfrm_probe,
  339. .remove = ufshcd_pltfrm_remove,
  340. .shutdown = ufshcd_pltfrm_shutdown,
  341. .driver = {
  342. .name = "ufshcd",
  343. .owner = THIS_MODULE,
  344. .pm = &ufshcd_dev_pm_ops,
  345. .of_match_table = ufs_of_match,
  346. },
  347. };
  348. module_platform_driver(ufshcd_pltfrm_driver);
  349. MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
  350. MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
  351. MODULE_DESCRIPTION("UFS host controller Pltform bus based glue driver");
  352. MODULE_LICENSE("GPL");
  353. MODULE_VERSION(UFSHCD_DRIVER_VERSION);