blob: ac77fb06bf78a4196b4029d3a717a93711d056a2 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +02002/*
3 * Qualcomm SDHCI driver - SD/eMMC controller
4 *
5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
6 *
7 * Based on Linux driver
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +02008 */
9
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020010#include <clk.h>
11#include <dm.h>
Simon Glass9bc15642020-02-03 07:36:16 -070012#include <malloc.h>
Varadarajan Narayanan035b9912025-02-26 12:15:04 +053013#include <reset.h>
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020014#include <sdhci.h>
15#include <wait_bit.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060016#include <asm/global_data.h>
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020017#include <asm/io.h>
18#include <linux/bitops.h>
Neil Armstrongf0e98c42024-10-16 11:17:16 +020019#include <power/regulator.h>
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020020
21/* Non-standard registers needed for SDHCI startup */
22#define SDCC_MCI_POWER 0x0
23#define SDCC_MCI_POWER_SW_RST BIT(7)
24
25/* This is undocumented register */
Sumit Garg1e2dc032022-07-12 12:42:09 +053026#define SDCC_MCI_VERSION 0x50
27#define SDCC_V5_VERSION 0x318
28
29#define SDCC_VERSION_MAJOR_SHIFT 28
30#define SDCC_VERSION_MAJOR_MASK (0xf << SDCC_VERSION_MAJOR_SHIFT)
31#define SDCC_VERSION_MINOR_MASK 0xff
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020032
33#define SDCC_MCI_STATUS2 0x6C
34#define SDCC_MCI_STATUS2_MCI_ACT 0x1
35#define SDCC_MCI_HC_MODE 0x78
36
Caleb Connolly3459a452024-06-21 03:53:09 +020037#define CORE_VENDOR_SPEC_POR_VAL 0xa9c
38
Simon Glass8ef07652016-06-12 23:30:29 -060039struct msm_sdhc_plat {
40 struct mmc_config cfg;
41 struct mmc mmc;
42};
43
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020044struct msm_sdhc {
45 struct sdhci_host host;
46 void *base;
Caleb Connollyfb782f52024-02-26 17:26:07 +000047 struct clk_bulk clks;
Neil Armstrongf0e98c42024-10-16 11:17:16 +020048 struct udevice *vqmmc;
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020049};
50
Sumit Garg1e2dc032022-07-12 12:42:09 +053051struct msm_sdhc_variant_info {
52 bool mci_removed;
Caleb Connollyc1f71d22024-04-09 20:03:00 +020053
Caleb Connolly3459a452024-06-21 03:53:09 +020054 u32 core_vendor_spec;
Caleb Connollyc1f71d22024-04-09 20:03:00 +020055 u32 core_vendor_spec_capabilities0;
Sumit Garg1e2dc032022-07-12 12:42:09 +053056};
57
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020058DECLARE_GLOBAL_DATA_PTR;
59
60static int msm_sdc_clk_init(struct udevice *dev)
61{
Caleb Connollyfb782f52024-02-26 17:26:07 +000062 struct msm_sdhc *prv = dev_get_priv(dev);
Caleb Connolly3459a452024-06-21 03:53:09 +020063 const struct msm_sdhc_variant_info *var_info;
Caleb Connollyfb782f52024-02-26 17:26:07 +000064 ofnode node = dev_ofnode(dev);
65 ulong clk_rate;
66 int ret, i = 0, n_clks;
67 const char *clk_name;
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020068
Caleb Connolly3459a452024-06-21 03:53:09 +020069 var_info = (void *)dev_get_driver_data(dev);
70
Caleb Connollyfb782f52024-02-26 17:26:07 +000071 ret = ofnode_read_u32(node, "clock-frequency", (uint *)(&clk_rate));
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020072 if (ret)
Caleb Connolly66dfa562024-04-09 20:03:03 +020073 clk_rate = 201500000;
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020074
Caleb Connollyfb782f52024-02-26 17:26:07 +000075 ret = clk_get_bulk(dev, &prv->clks);
76 if (ret) {
77 log_warning("Couldn't get mmc clocks: %d\n", ret);
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020078 return ret;
Caleb Connollyfb782f52024-02-26 17:26:07 +000079 }
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +020080
Caleb Connollyfb782f52024-02-26 17:26:07 +000081 ret = clk_enable_bulk(&prv->clks);
82 if (ret) {
83 log_warning("Couldn't enable mmc clocks: %d\n", ret);
Stephen Warrena9622432016-06-17 09:44:00 -060084 return ret;
Caleb Connollyfb782f52024-02-26 17:26:07 +000085 }
Stephen Warrena9622432016-06-17 09:44:00 -060086
Caleb Connollyfb782f52024-02-26 17:26:07 +000087 /* If clock-names is unspecified, then the first clock is the core clock */
88 if (!ofnode_get_property(node, "clock-names", &n_clks)) {
89 if (!clk_set_rate(&prv->clks.clks[0], clk_rate)) {
90 log_warning("Couldn't set core clock rate: %d\n", ret);
91 return -EINVAL;
92 }
93 }
94
95 /* Find the index of the "core" clock */
96 while (i < n_clks) {
97 ofnode_read_string_index(node, "clock-names", i, &clk_name);
98 if (!strcmp(clk_name, "core"))
99 break;
100 i++;
101 }
102
103 if (i >= prv->clks.count) {
104 log_warning("Couldn't find core clock (index %d but only have %d clocks)\n", i,
105 prv->clks.count);
106 return -EINVAL;
107 }
108
109 /* The clock is already enabled by the clk_bulk above */
110 clk_rate = clk_set_rate(&prv->clks.clks[i], clk_rate);
111 /* If we get a rate of 0 then something has probably gone wrong. */
112 if (clk_rate == 0 || IS_ERR((void *)clk_rate)) {
113 log_warning("Couldn't set MMC core clock rate: %dE\n", clk_rate ? (int)PTR_ERR((void *)clk_rate) : 0);
114 return -EINVAL;
115 }
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200116
Caleb Connolly3459a452024-06-21 03:53:09 +0200117 writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
118 prv->host.ioaddr + var_info->core_vendor_spec);
119
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200120 return 0;
121}
122
Sumit Garg1e2dc032022-07-12 12:42:09 +0530123static int msm_sdc_mci_init(struct msm_sdhc *prv)
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200124{
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200125 /* Reset the core and Enable SDHC mode */
126 writel(readl(prv->base + SDCC_MCI_POWER) | SDCC_MCI_POWER_SW_RST,
127 prv->base + SDCC_MCI_POWER);
128
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200129 /* Wait for reset to be written to register */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100130 if (wait_for_bit_le32(prv->base + SDCC_MCI_STATUS2,
131 SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) {
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200132 printf("msm_sdhci: reset request failed\n");
133 return -EIO;
134 }
135
136 /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
Álvaro Fernández Rojas918de032018-01-23 17:14:55 +0100137 if (wait_for_bit_le32(prv->base + SDCC_MCI_POWER,
138 SDCC_MCI_POWER_SW_RST, false, 2, false)) {
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200139 printf("msm_sdhci: stuck in reset\n");
140 return -ETIMEDOUT;
141 }
142
143 /* Enable host-controller mode */
144 writel(1, prv->base + SDCC_MCI_HC_MODE);
145
Sumit Garg1e2dc032022-07-12 12:42:09 +0530146 return 0;
147}
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200148
Sumit Garg1e2dc032022-07-12 12:42:09 +0530149static int msm_sdc_probe(struct udevice *dev)
150{
151 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
152 struct msm_sdhc_plat *plat = dev_get_plat(dev);
153 struct msm_sdhc *prv = dev_get_priv(dev);
154 const struct msm_sdhc_variant_info *var_info;
155 struct sdhci_host *host = &prv->host;
156 u32 core_version, core_minor, core_major;
Varadarajan Narayanan035b9912025-02-26 12:15:04 +0530157 struct reset_ctl bcr_rst;
Sumit Garg1e2dc032022-07-12 12:42:09 +0530158 u32 caps;
159 int ret;
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200160
Varadarajan Narayanan035b9912025-02-26 12:15:04 +0530161 ret = reset_get_by_index(dev, 0, &bcr_rst);
162 if (!ret) {
163 reset_assert(&bcr_rst);
164 udelay(200);
165 reset_deassert(&bcr_rst);
166 udelay(200);
167 }
168
Sumit Garg1e2dc032022-07-12 12:42:09 +0530169 host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B;
170
171 host->max_clk = 0;
172
173 /* Init clocks */
174 ret = msm_sdc_clk_init(dev);
175 if (ret)
176 return ret;
177
Neil Armstrongf0e98c42024-10-16 11:17:16 +0200178 /* Get the vqmmc regulator and enable it if available */
179 device_get_supply_regulator(dev, "vqmmc-supply", &prv->vqmmc);
180 if (prv->vqmmc) {
181 ret = regulator_set_enable_if_allowed(prv->vqmmc, true);
182 if (ret) {
183 printf("Failed to enable the VQMMC regulator\n");
184 return ret;
185 }
186 }
187
Sumit Garg1e2dc032022-07-12 12:42:09 +0530188 var_info = (void *)dev_get_driver_data(dev);
189 if (!var_info->mci_removed) {
190 ret = msm_sdc_mci_init(prv);
191 if (ret)
192 return ret;
193 }
194
195 if (!var_info->mci_removed)
196 core_version = readl(prv->base + SDCC_MCI_VERSION);
197 else
198 core_version = readl(host->ioaddr + SDCC_V5_VERSION);
199
200 core_major = (core_version & SDCC_VERSION_MAJOR_MASK);
201 core_major >>= SDCC_VERSION_MAJOR_SHIFT;
202
203 core_minor = core_version & SDCC_VERSION_MINOR_MASK;
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200204
Caleb Connolly790d4122024-04-09 20:03:02 +0200205 log_debug("SDCC version %d.%d\n", core_major, core_minor);
206
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200207 /*
208 * Support for some capabilities is not advertised by newer
209 * controller versions and must be explicitly enabled.
210 */
211 if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
Simon Glass8ef07652016-06-12 23:30:29 -0600212 caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200213 caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
Caleb Connollyc1f71d22024-04-09 20:03:00 +0200214 writel(caps, host->ioaddr + var_info->core_vendor_spec_capabilities0);
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200215 }
216
Manivannan Sadhasivam6b36ab52020-07-16 14:37:26 +0530217 ret = mmc_of_parse(dev, &plat->cfg);
218 if (ret)
219 return ret;
220
Simon Glass8ef07652016-06-12 23:30:29 -0600221 host->mmc = &plat->mmc;
Peng Fanf92f7b62019-08-06 02:47:53 +0000222 host->mmc->dev = dev;
223 ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
Mateusz Kulikowskic012e572016-06-26 22:43:55 +0200224 if (ret)
225 return ret;
Simon Glass8ef07652016-06-12 23:30:29 -0600226 host->mmc->priv = &prv->host;
Simon Glass8ef07652016-06-12 23:30:29 -0600227 upriv->mmc = host->mmc;
Mateusz Kulikowskic012e572016-06-26 22:43:55 +0200228
Simon Glass8ef07652016-06-12 23:30:29 -0600229 return sdhci_probe(dev);
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200230}
231
232static int msm_sdc_remove(struct udevice *dev)
233{
234 struct msm_sdhc *priv = dev_get_priv(dev);
Sumit Garg1e2dc032022-07-12 12:42:09 +0530235 const struct msm_sdhc_variant_info *var_info;
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200236
Sumit Garg1e2dc032022-07-12 12:42:09 +0530237 var_info = (void *)dev_get_driver_data(dev);
238
239 /* Disable host-controller mode */
Caleb Connolly6d32da32024-04-09 20:03:01 +0200240 if (!var_info->mci_removed && priv->base)
Sumit Garg1e2dc032022-07-12 12:42:09 +0530241 writel(0, priv->base + SDCC_MCI_HC_MODE);
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200242
Caleb Connollyfb782f52024-02-26 17:26:07 +0000243 clk_release_bulk(&priv->clks);
244
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200245 return 0;
246}
247
Simon Glassaad29ae2020-12-03 16:55:21 -0700248static int msm_of_to_plat(struct udevice *dev)
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200249{
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200250 struct msm_sdhc *priv = dev_get_priv(dev);
Caleb Connolly6d32da32024-04-09 20:03:01 +0200251 const struct msm_sdhc_variant_info *var_info;
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200252 struct sdhci_host *host = &priv->host;
Caleb Connolly6d32da32024-04-09 20:03:01 +0200253 int ret;
254
255 var_info = (void*)dev_get_driver_data(dev);
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200256
257 host->name = strdup(dev->name);
Masahiro Yamada1096ae12020-07-17 14:36:46 +0900258 host->ioaddr = dev_read_addr_ptr(dev);
Caleb Connolly6d32da32024-04-09 20:03:01 +0200259 ret = dev_read_u32(dev, "bus-width", &host->bus_width);
260 if (ret)
261 host->bus_width = 4;
262 ret = dev_read_u32(dev, "index", &host->index);
263 if (ret)
264 host->index = 0;
265 priv->base = dev_read_addr_index_ptr(dev, 1);
266
267 if (!host->ioaddr)
268 return -EINVAL;
269
270 if (!var_info->mci_removed && !priv->base) {
271 printf("msm_sdhci: MCI base address not found\n");
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200272 return -EINVAL;
Caleb Connolly6d32da32024-04-09 20:03:01 +0200273 }
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200274
275 return 0;
276}
277
Simon Glass8ef07652016-06-12 23:30:29 -0600278static int msm_sdc_bind(struct udevice *dev)
279{
Simon Glassfa20e932020-12-03 16:55:20 -0700280 struct msm_sdhc_plat *plat = dev_get_plat(dev);
Simon Glass8ef07652016-06-12 23:30:29 -0600281
Masahiro Yamadacdb67f32016-09-06 22:17:32 +0900282 return sdhci_bind(dev, &plat->mmc, &plat->cfg);
Simon Glass8ef07652016-06-12 23:30:29 -0600283}
284
Sumit Garg1e2dc032022-07-12 12:42:09 +0530285static const struct msm_sdhc_variant_info msm_sdhc_mci_var = {
286 .mci_removed = false,
Caleb Connollyc1f71d22024-04-09 20:03:00 +0200287
Caleb Connolly3459a452024-06-21 03:53:09 +0200288 .core_vendor_spec = 0x10c,
Caleb Connolly5d8b5752024-04-12 20:10:21 +0200289 .core_vendor_spec_capabilities0 = 0x11c,
Sumit Garg1e2dc032022-07-12 12:42:09 +0530290};
291
292static const struct msm_sdhc_variant_info msm_sdhc_v5_var = {
293 .mci_removed = true,
Caleb Connollyc1f71d22024-04-09 20:03:00 +0200294
Caleb Connolly3459a452024-06-21 03:53:09 +0200295 .core_vendor_spec = 0x20c,
Caleb Connolly5d8b5752024-04-12 20:10:21 +0200296 .core_vendor_spec_capabilities0 = 0x21c,
Sumit Garg1e2dc032022-07-12 12:42:09 +0530297};
298
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200299static const struct udevice_id msm_mmc_ids[] = {
Sumit Garg1e2dc032022-07-12 12:42:09 +0530300 { .compatible = "qcom,sdhci-msm-v4", .data = (ulong)&msm_sdhc_mci_var },
301 { .compatible = "qcom,sdhci-msm-v5", .data = (ulong)&msm_sdhc_v5_var },
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200302 { }
303};
304
305U_BOOT_DRIVER(msm_sdc_drv) = {
306 .name = "msm_sdc",
307 .id = UCLASS_MMC,
308 .of_match = msm_mmc_ids,
Simon Glassaad29ae2020-12-03 16:55:21 -0700309 .of_to_plat = msm_of_to_plat,
Simon Glass8ef07652016-06-12 23:30:29 -0600310 .ops = &sdhci_ops,
Simon Glass8ef07652016-06-12 23:30:29 -0600311 .bind = msm_sdc_bind,
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200312 .probe = msm_sdc_probe,
313 .remove = msm_sdc_remove,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700314 .priv_auto = sizeof(struct msm_sdhc),
Simon Glass71fa5b42020-12-03 16:55:18 -0700315 .plat_auto = sizeof(struct msm_sdhc_plat),
Mateusz Kulikowskia00b0c02016-03-31 23:12:16 +0200316};