ufshcd-pltfrm.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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. static int ufshcd_pltfrm_runtime_suspend(struct device *dev)
  230. {
  231. return ufshcd_runtime_suspend(dev_get_drvdata(dev));
  232. }
  233. static int ufshcd_pltfrm_runtime_resume(struct device *dev)
  234. {
  235. return ufshcd_runtime_resume(dev_get_drvdata(dev));
  236. }
  237. static int ufshcd_pltfrm_runtime_idle(struct device *dev)
  238. {
  239. return ufshcd_runtime_idle(dev_get_drvdata(dev));
  240. }
  241. #else /* !CONFIG_PM */
  242. #define ufshcd_pltfrm_suspend NULL
  243. #define ufshcd_pltfrm_resume NULL
  244. #define ufshcd_pltfrm_runtime_suspend NULL
  245. #define ufshcd_pltfrm_runtime_resume NULL
  246. #define ufshcd_pltfrm_runtime_idle NULL
  247. #endif /* CONFIG_PM */
  248. static void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
  249. {
  250. ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
  251. }
  252. /**
  253. * ufshcd_pltfrm_probe - probe routine of the driver
  254. * @pdev: pointer to Platform device handle
  255. *
  256. * Returns 0 on success, non-zero value on failure
  257. */
  258. static int ufshcd_pltfrm_probe(struct platform_device *pdev)
  259. {
  260. struct ufs_hba *hba;
  261. void __iomem *mmio_base;
  262. struct resource *mem_res;
  263. int irq, err;
  264. struct device *dev = &pdev->dev;
  265. mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  266. mmio_base = devm_ioremap_resource(dev, mem_res);
  267. if (IS_ERR(*(void **)&mmio_base)) {
  268. err = PTR_ERR(*(void **)&mmio_base);
  269. goto out;
  270. }
  271. irq = platform_get_irq(pdev, 0);
  272. if (irq < 0) {
  273. dev_err(dev, "IRQ resource not available\n");
  274. err = -ENODEV;
  275. goto out;
  276. }
  277. err = ufshcd_alloc_host(dev, &hba);
  278. if (err) {
  279. dev_err(&pdev->dev, "Allocation failed\n");
  280. goto out;
  281. }
  282. hba->vops = get_variant_ops(&pdev->dev);
  283. err = ufshcd_parse_clock_info(hba);
  284. if (err) {
  285. dev_err(&pdev->dev, "%s: clock parse failed %d\n",
  286. __func__, err);
  287. goto out;
  288. }
  289. err = ufshcd_parse_regulator_info(hba);
  290. if (err) {
  291. dev_err(&pdev->dev, "%s: regulator init failed %d\n",
  292. __func__, err);
  293. goto out;
  294. }
  295. pm_runtime_set_active(&pdev->dev);
  296. pm_runtime_enable(&pdev->dev);
  297. err = ufshcd_init(hba, mmio_base, irq);
  298. if (err) {
  299. dev_err(dev, "Intialization failed\n");
  300. goto out_disable_rpm;
  301. }
  302. platform_set_drvdata(pdev, hba);
  303. return 0;
  304. out_disable_rpm:
  305. pm_runtime_disable(&pdev->dev);
  306. pm_runtime_set_suspended(&pdev->dev);
  307. out:
  308. return err;
  309. }
  310. /**
  311. * ufshcd_pltfrm_remove - remove platform driver routine
  312. * @pdev: pointer to platform device handle
  313. *
  314. * Returns 0 on success, non-zero value on failure
  315. */
  316. static int ufshcd_pltfrm_remove(struct platform_device *pdev)
  317. {
  318. struct ufs_hba *hba = platform_get_drvdata(pdev);
  319. pm_runtime_get_sync(&(pdev)->dev);
  320. ufshcd_remove(hba);
  321. return 0;
  322. }
  323. static const struct of_device_id ufs_of_match[] = {
  324. { .compatible = "jedec,ufs-1.1"},
  325. {},
  326. };
  327. static const struct dev_pm_ops ufshcd_dev_pm_ops = {
  328. .suspend = ufshcd_pltfrm_suspend,
  329. .resume = ufshcd_pltfrm_resume,
  330. .runtime_suspend = ufshcd_pltfrm_runtime_suspend,
  331. .runtime_resume = ufshcd_pltfrm_runtime_resume,
  332. .runtime_idle = ufshcd_pltfrm_runtime_idle,
  333. };
  334. static struct platform_driver ufshcd_pltfrm_driver = {
  335. .probe = ufshcd_pltfrm_probe,
  336. .remove = ufshcd_pltfrm_remove,
  337. .shutdown = ufshcd_pltfrm_shutdown,
  338. .driver = {
  339. .name = "ufshcd",
  340. .pm = &ufshcd_dev_pm_ops,
  341. .of_match_table = ufs_of_match,
  342. },
  343. };
  344. module_platform_driver(ufshcd_pltfrm_driver);
  345. MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
  346. MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
  347. MODULE_DESCRIPTION("UFS host controller Pltform bus based glue driver");
  348. MODULE_LICENSE("GPL");
  349. MODULE_VERSION(UFSHCD_DRIVER_VERSION);