xref: /openbmc/u-boot/drivers/mmc/msm_sdhci.c (revision ee7bb5be)
1 /*
2  * Qualcomm SDHCI driver - SD/eMMC controller
3  *
4  * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
5  *
6  * Based on Linux driver
7  *
8  * SPDX-License-Identifier:	GPL-2.0+
9  */
10 
11 #include <common.h>
12 #include <clk.h>
13 #include <dm.h>
14 #include <sdhci.h>
15 #include <wait_bit.h>
16 #include <asm/io.h>
17 #include <linux/bitops.h>
18 
19 /* Non-standard registers needed for SDHCI startup */
20 #define SDCC_MCI_POWER   0x0
21 #define SDCC_MCI_POWER_SW_RST BIT(7)
22 
23 /* This is undocumented register */
24 #define SDCC_MCI_VERSION             0x50
25 #define SDCC_MCI_VERSION_MAJOR_SHIFT 28
26 #define SDCC_MCI_VERSION_MAJOR_MASK  (0xf << SDCC_MCI_VERSION_MAJOR_SHIFT)
27 #define SDCC_MCI_VERSION_MINOR_MASK  0xff
28 
29 #define SDCC_MCI_STATUS2 0x6C
30 #define SDCC_MCI_STATUS2_MCI_ACT 0x1
31 #define SDCC_MCI_HC_MODE 0x78
32 
33 /* Offset to SDHCI registers */
34 #define SDCC_SDHCI_OFFSET 0x900
35 
36 /* Non standard (?) SDHCI register */
37 #define SDHCI_VENDOR_SPEC_CAPABILITIES0  0x11c
38 
39 struct msm_sdhc {
40 	struct sdhci_host host;
41 	void *base;
42 };
43 
44 DECLARE_GLOBAL_DATA_PTR;
45 
46 static int msm_sdc_clk_init(struct udevice *dev)
47 {
48 	uint clk_rate = fdtdec_get_uint(gd->fdt_blob, dev->of_offset,
49 					"clock-frequency", 400000);
50 	uint clkd[2]; /* clk_id and clk_no */
51 	int clk_offset;
52 	struct udevice *clk;
53 	int ret;
54 
55 	ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, "clock", clkd,
56 				   2);
57 	if (ret)
58 		return ret;
59 
60 	clk_offset = fdt_node_offset_by_phandle(gd->fdt_blob, clkd[0]);
61 	if (clk_offset < 0)
62 		return clk_offset;
63 
64 	ret = uclass_get_device_by_of_offset(UCLASS_CLK, clk_offset, &clk);
65 	if (ret)
66 		return ret;
67 
68 	ret = clk_set_periph_rate(clk, clkd[1], clk_rate);
69 	if (ret < 0)
70 		return ret;
71 
72 	return 0;
73 }
74 
75 static int msm_sdc_probe(struct udevice *dev)
76 {
77 	struct msm_sdhc *prv = dev_get_priv(dev);
78 	struct sdhci_host *host = &prv->host;
79 	u32 core_version, core_minor, core_major;
80 	int ret;
81 
82 	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_BROKEN_R1B;
83 
84 	/* Init clocks */
85 	ret = msm_sdc_clk_init(dev);
86 	if (ret)
87 		return ret;
88 
89 	/* Reset the core and Enable SDHC mode */
90 	writel(readl(prv->base + SDCC_MCI_POWER) | SDCC_MCI_POWER_SW_RST,
91 	       prv->base + SDCC_MCI_POWER);
92 
93 
94 	/* Wait for reset to be written to register */
95 	if (wait_for_bit(__func__, prv->base + SDCC_MCI_STATUS2,
96 			 SDCC_MCI_STATUS2_MCI_ACT, false, 10, false)) {
97 		printf("msm_sdhci: reset request failed\n");
98 		return -EIO;
99 	}
100 
101 	/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
102 	if (wait_for_bit(__func__, prv->base + SDCC_MCI_POWER,
103 			 SDCC_MCI_POWER_SW_RST, false, 2, false)) {
104 		printf("msm_sdhci: stuck in reset\n");
105 		return -ETIMEDOUT;
106 	}
107 
108 	/* Enable host-controller mode */
109 	writel(1, prv->base + SDCC_MCI_HC_MODE);
110 
111 	core_version = readl(prv->base + SDCC_MCI_VERSION);
112 
113 	core_major = (core_version & SDCC_MCI_VERSION_MAJOR_MASK);
114 	core_major >>= SDCC_MCI_VERSION_MAJOR_SHIFT;
115 
116 	core_minor = core_version & SDCC_MCI_VERSION_MINOR_MASK;
117 
118 	/*
119 	 * Support for some capabilities is not advertised by newer
120 	 * controller versions and must be explicitly enabled.
121 	 */
122 	if (core_major >= 1 && core_minor != 0x11 && core_minor != 0x12) {
123 		u32 caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
124 		caps |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT;
125 		writel(caps, host->ioaddr + SDHCI_VENDOR_SPEC_CAPABILITIES0);
126 	}
127 
128 	/* Set host controller version */
129 	host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
130 
131 	/* automatically detect max and min speed */
132 	return add_sdhci(host, 0, 0);
133 }
134 
135 static int msm_sdc_remove(struct udevice *dev)
136 {
137 	struct msm_sdhc *priv = dev_get_priv(dev);
138 
139 	 /* Disable host-controller mode */
140 	writel(0, priv->base + SDCC_MCI_HC_MODE);
141 
142 	return 0;
143 }
144 
145 static int msm_ofdata_to_platdata(struct udevice *dev)
146 {
147 	struct udevice *parent = dev->parent;
148 	struct msm_sdhc *priv = dev_get_priv(dev);
149 	struct sdhci_host *host = &priv->host;
150 
151 	host->name = strdup(dev->name);
152 	host->ioaddr = (void *)dev_get_addr(dev);
153 	host->bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
154 					 "bus-width", 4);
155 	host->index = fdtdec_get_uint(gd->fdt_blob, dev->of_offset, "index", 0);
156 	priv->base = (void *)fdtdec_get_addr_size_auto_parent(gd->fdt_blob,
157 							      parent->of_offset,
158 							      dev->of_offset,
159 							      "reg", 1, NULL);
160 	if (priv->base == (void *)FDT_ADDR_T_NONE ||
161 	    host->ioaddr == (void *)FDT_ADDR_T_NONE)
162 		return -EINVAL;
163 
164 	return 0;
165 }
166 
167 static const struct udevice_id msm_mmc_ids[] = {
168 	{ .compatible = "qcom,sdhci-msm-v4" },
169 	{ }
170 };
171 
172 U_BOOT_DRIVER(msm_sdc_drv) = {
173 	.name		= "msm_sdc",
174 	.id		= UCLASS_MMC,
175 	.of_match	= msm_mmc_ids,
176 	.ofdata_to_platdata = msm_ofdata_to_platdata,
177 	.probe		= msm_sdc_probe,
178 	.remove		= msm_sdc_remove,
179 	.priv_auto_alloc_size = sizeof(struct msm_sdhc),
180 };
181