sdhci-milbeaut.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
  4. * Vincent Yang <[email protected]>
  5. * Copyright (C) 2015 Linaro Ltd Andy Green <[email protected]>
  6. * Copyright (C) 2019 Socionext Inc.
  7. * Takao Orito <[email protected]>
  8. */
  9. #include <linux/bits.h>
  10. #include <linux/clk.h>
  11. #include <linux/delay.h>
  12. #include <linux/err.h>
  13. #include <linux/gpio/consumer.h>
  14. #include <linux/module.h>
  15. #include <linux/of.h>
  16. #include <linux/property.h>
  17. #include "sdhci-pltfm.h"
  18. #include "sdhci_f_sdh30.h"
  19. /* milbeaut bridge controller register */
  20. #define MLB_SOFT_RESET 0x0200
  21. #define MLB_SOFT_RESET_RSTX BIT(0)
  22. #define MLB_WP_CD_LED_SET 0x0210
  23. #define MLB_WP_CD_LED_SET_LED_INV BIT(2)
  24. #define MLB_CR_SET 0x0220
  25. #define MLB_CR_SET_CR_TOCLKUNIT BIT(24)
  26. #define MLB_CR_SET_CR_TOCLKFREQ_SFT (16)
  27. #define MLB_CR_SET_CR_TOCLKFREQ_MASK (0x3F << MLB_CR_SET_CR_TOCLKFREQ_SFT)
  28. #define MLB_CR_SET_CR_BCLKFREQ_SFT (8)
  29. #define MLB_CR_SET_CR_BCLKFREQ_MASK (0xFF << MLB_CR_SET_CR_BCLKFREQ_SFT)
  30. #define MLB_CR_SET_CR_RTUNTIMER_SFT (4)
  31. #define MLB_CR_SET_CR_RTUNTIMER_MASK (0xF << MLB_CR_SET_CR_RTUNTIMER_SFT)
  32. #define MLB_SD_TOCLK_I_DIV 16
  33. #define MLB_TOCLKFREQ_UNIT_THRES 16000000
  34. #define MLB_CAL_TOCLKFREQ_MHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000000)
  35. #define MLB_CAL_TOCLKFREQ_KHZ(rate) (rate / MLB_SD_TOCLK_I_DIV / 1000)
  36. #define MLB_TOCLKFREQ_MAX 63
  37. #define MLB_TOCLKFREQ_MIN 1
  38. #define MLB_SD_BCLK_I_DIV 4
  39. #define MLB_CAL_BCLKFREQ(rate) (rate / MLB_SD_BCLK_I_DIV / 1000000)
  40. #define MLB_BCLKFREQ_MAX 255
  41. #define MLB_BCLKFREQ_MIN 1
  42. #define MLB_CDR_SET 0x0230
  43. #define MLB_CDR_SET_CLK2POW16 3
  44. struct f_sdhost_priv {
  45. struct clk *clk_iface;
  46. struct clk *clk;
  47. struct device *dev;
  48. bool enable_cmd_dat_delay;
  49. };
  50. static void sdhci_milbeaut_soft_voltage_switch(struct sdhci_host *host)
  51. {
  52. u32 ctrl = 0;
  53. usleep_range(2500, 3000);
  54. ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
  55. ctrl |= F_SDH30_CRES_O_DN;
  56. sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
  57. ctrl |= F_SDH30_MSEL_O_1_8;
  58. sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
  59. ctrl &= ~F_SDH30_CRES_O_DN;
  60. sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
  61. usleep_range(2500, 3000);
  62. ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
  63. ctrl |= F_SDH30_CMD_CHK_DIS;
  64. sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
  65. }
  66. static unsigned int sdhci_milbeaut_get_min_clock(struct sdhci_host *host)
  67. {
  68. return F_SDH30_MIN_CLOCK;
  69. }
  70. static void sdhci_milbeaut_reset(struct sdhci_host *host, u8 mask)
  71. {
  72. struct f_sdhost_priv *priv = sdhci_priv(host);
  73. u16 clk;
  74. u32 ctl;
  75. ktime_t timeout;
  76. clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
  77. clk = (clk & ~SDHCI_CLOCK_CARD_EN) | SDHCI_CLOCK_INT_EN;
  78. sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
  79. sdhci_reset(host, mask);
  80. clk |= SDHCI_CLOCK_CARD_EN;
  81. sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
  82. timeout = ktime_add_ms(ktime_get(), 10);
  83. while (1) {
  84. bool timedout = ktime_after(ktime_get(), timeout);
  85. clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
  86. if (clk & SDHCI_CLOCK_INT_STABLE)
  87. break;
  88. if (timedout) {
  89. pr_err("%s: Internal clock never stabilised.\n",
  90. mmc_hostname(host->mmc));
  91. sdhci_dumpregs(host);
  92. return;
  93. }
  94. udelay(10);
  95. }
  96. if (priv->enable_cmd_dat_delay) {
  97. ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
  98. ctl |= F_SDH30_CMD_DAT_DELAY;
  99. sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL);
  100. }
  101. }
  102. static const struct sdhci_ops sdhci_milbeaut_ops = {
  103. .voltage_switch = sdhci_milbeaut_soft_voltage_switch,
  104. .get_min_clock = sdhci_milbeaut_get_min_clock,
  105. .reset = sdhci_milbeaut_reset,
  106. .set_clock = sdhci_set_clock,
  107. .set_bus_width = sdhci_set_bus_width,
  108. .set_uhs_signaling = sdhci_set_uhs_signaling,
  109. .set_power = sdhci_set_power_and_bus_voltage,
  110. };
  111. static void sdhci_milbeaut_bridge_reset(struct sdhci_host *host,
  112. int reset_flag)
  113. {
  114. if (reset_flag)
  115. sdhci_writel(host, 0, MLB_SOFT_RESET);
  116. else
  117. sdhci_writel(host, MLB_SOFT_RESET_RSTX, MLB_SOFT_RESET);
  118. }
  119. static void sdhci_milbeaut_bridge_init(struct sdhci_host *host,
  120. int rate)
  121. {
  122. u32 val, clk;
  123. /* IO_SDIO_CR_SET should be set while reset */
  124. val = sdhci_readl(host, MLB_CR_SET);
  125. val &= ~(MLB_CR_SET_CR_TOCLKFREQ_MASK | MLB_CR_SET_CR_TOCLKUNIT |
  126. MLB_CR_SET_CR_BCLKFREQ_MASK);
  127. if (rate >= MLB_TOCLKFREQ_UNIT_THRES) {
  128. clk = MLB_CAL_TOCLKFREQ_MHZ(rate);
  129. clk = min_t(u32, MLB_TOCLKFREQ_MAX, clk);
  130. val |= MLB_CR_SET_CR_TOCLKUNIT |
  131. (clk << MLB_CR_SET_CR_TOCLKFREQ_SFT);
  132. } else {
  133. clk = MLB_CAL_TOCLKFREQ_KHZ(rate);
  134. clk = min_t(u32, MLB_TOCLKFREQ_MAX, clk);
  135. clk = max_t(u32, MLB_TOCLKFREQ_MIN, clk);
  136. val |= clk << MLB_CR_SET_CR_TOCLKFREQ_SFT;
  137. }
  138. clk = MLB_CAL_BCLKFREQ(rate);
  139. clk = min_t(u32, MLB_BCLKFREQ_MAX, clk);
  140. clk = max_t(u32, MLB_BCLKFREQ_MIN, clk);
  141. val |= clk << MLB_CR_SET_CR_BCLKFREQ_SFT;
  142. val &= ~MLB_CR_SET_CR_RTUNTIMER_MASK;
  143. sdhci_writel(host, val, MLB_CR_SET);
  144. sdhci_writel(host, MLB_CDR_SET_CLK2POW16, MLB_CDR_SET);
  145. sdhci_writel(host, MLB_WP_CD_LED_SET_LED_INV, MLB_WP_CD_LED_SET);
  146. }
  147. static void sdhci_milbeaut_vendor_init(struct sdhci_host *host)
  148. {
  149. struct f_sdhost_priv *priv = sdhci_priv(host);
  150. u32 ctl;
  151. ctl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
  152. ctl |= F_SDH30_CRES_O_DN;
  153. sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2);
  154. ctl &= ~F_SDH30_MSEL_O_1_8;
  155. sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2);
  156. ctl &= ~F_SDH30_CRES_O_DN;
  157. sdhci_writel(host, ctl, F_SDH30_IO_CONTROL2);
  158. ctl = sdhci_readw(host, F_SDH30_AHB_CONFIG);
  159. ctl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 |
  160. F_SDH30_AHB_INCR_4;
  161. ctl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN);
  162. sdhci_writew(host, ctl, F_SDH30_AHB_CONFIG);
  163. if (priv->enable_cmd_dat_delay) {
  164. ctl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
  165. ctl |= F_SDH30_CMD_DAT_DELAY;
  166. sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL);
  167. }
  168. }
  169. static const struct of_device_id mlb_dt_ids[] = {
  170. {
  171. .compatible = "socionext,milbeaut-m10v-sdhci-3.0",
  172. },
  173. { /* sentinel */ }
  174. };
  175. MODULE_DEVICE_TABLE(of, mlb_dt_ids);
  176. static void sdhci_milbeaut_init(struct sdhci_host *host)
  177. {
  178. struct f_sdhost_priv *priv = sdhci_priv(host);
  179. int rate = clk_get_rate(priv->clk);
  180. u16 ctl;
  181. sdhci_milbeaut_bridge_reset(host, 0);
  182. ctl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
  183. ctl &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
  184. sdhci_writew(host, ctl, SDHCI_CLOCK_CONTROL);
  185. sdhci_milbeaut_bridge_reset(host, 1);
  186. sdhci_milbeaut_bridge_init(host, rate);
  187. sdhci_milbeaut_bridge_reset(host, 0);
  188. sdhci_milbeaut_vendor_init(host);
  189. }
  190. static int sdhci_milbeaut_probe(struct platform_device *pdev)
  191. {
  192. struct sdhci_host *host;
  193. struct device *dev = &pdev->dev;
  194. int irq, ret = 0;
  195. struct f_sdhost_priv *priv;
  196. irq = platform_get_irq(pdev, 0);
  197. if (irq < 0)
  198. return irq;
  199. host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv));
  200. if (IS_ERR(host))
  201. return PTR_ERR(host);
  202. priv = sdhci_priv(host);
  203. priv->dev = dev;
  204. host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
  205. SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
  206. SDHCI_QUIRK_CLOCK_BEFORE_RESET |
  207. SDHCI_QUIRK_DELAY_AFTER_POWER;
  208. host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
  209. SDHCI_QUIRK2_TUNING_WORK_AROUND |
  210. SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
  211. priv->enable_cmd_dat_delay = device_property_read_bool(dev,
  212. "fujitsu,cmd-dat-delay-select");
  213. ret = mmc_of_parse(host->mmc);
  214. if (ret)
  215. goto err;
  216. platform_set_drvdata(pdev, host);
  217. host->hw_name = "f_sdh30";
  218. host->ops = &sdhci_milbeaut_ops;
  219. host->irq = irq;
  220. host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
  221. if (IS_ERR(host->ioaddr)) {
  222. ret = PTR_ERR(host->ioaddr);
  223. goto err;
  224. }
  225. if (dev_of_node(dev)) {
  226. sdhci_get_of_property(pdev);
  227. priv->clk_iface = devm_clk_get(&pdev->dev, "iface");
  228. if (IS_ERR(priv->clk_iface)) {
  229. ret = PTR_ERR(priv->clk_iface);
  230. goto err;
  231. }
  232. ret = clk_prepare_enable(priv->clk_iface);
  233. if (ret)
  234. goto err;
  235. priv->clk = devm_clk_get(&pdev->dev, "core");
  236. if (IS_ERR(priv->clk)) {
  237. ret = PTR_ERR(priv->clk);
  238. goto err_clk;
  239. }
  240. ret = clk_prepare_enable(priv->clk);
  241. if (ret)
  242. goto err_clk;
  243. }
  244. sdhci_milbeaut_init(host);
  245. ret = sdhci_add_host(host);
  246. if (ret)
  247. goto err_add_host;
  248. return 0;
  249. err_add_host:
  250. clk_disable_unprepare(priv->clk);
  251. err_clk:
  252. clk_disable_unprepare(priv->clk_iface);
  253. err:
  254. sdhci_free_host(host);
  255. return ret;
  256. }
  257. static int sdhci_milbeaut_remove(struct platform_device *pdev)
  258. {
  259. struct sdhci_host *host = platform_get_drvdata(pdev);
  260. struct f_sdhost_priv *priv = sdhci_priv(host);
  261. sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
  262. 0xffffffff);
  263. clk_disable_unprepare(priv->clk_iface);
  264. clk_disable_unprepare(priv->clk);
  265. sdhci_free_host(host);
  266. platform_set_drvdata(pdev, NULL);
  267. return 0;
  268. }
  269. static struct platform_driver sdhci_milbeaut_driver = {
  270. .driver = {
  271. .name = "sdhci-milbeaut",
  272. .probe_type = PROBE_PREFER_ASYNCHRONOUS,
  273. .of_match_table = of_match_ptr(mlb_dt_ids),
  274. },
  275. .probe = sdhci_milbeaut_probe,
  276. .remove = sdhci_milbeaut_remove,
  277. };
  278. module_platform_driver(sdhci_milbeaut_driver);
  279. MODULE_DESCRIPTION("MILBEAUT SD Card Controller driver");
  280. MODULE_AUTHOR("Takao Orito <[email protected]>");
  281. MODULE_LICENSE("GPL v2");
  282. MODULE_ALIAS("platform:sdhci-milbeaut");