xref: /rk3399_rockchip-uboot/drivers/mmc/atmel_sdhci.c (revision a821c4af79e4f5ce9b629b20473863397bbe9b10)
1a3b59b15SWenyou Yang /*
2a3b59b15SWenyou Yang  * Copyright (C) 2015 Atmel Corporation
3a3b59b15SWenyou Yang  *		      Wenyou.Yang <wenyou.yang@atmel.com>
4a3b59b15SWenyou Yang  *
5a3b59b15SWenyou Yang  * SPDX-License-Identifier:	GPL-2.0+
6a3b59b15SWenyou Yang  */
7a3b59b15SWenyou Yang 
8a3b59b15SWenyou Yang #include <common.h>
9a0d0d86fSWenyou Yang #include <clk.h>
10a0d0d86fSWenyou Yang #include <dm.h>
11a3b59b15SWenyou Yang #include <malloc.h>
12a3b59b15SWenyou Yang #include <sdhci.h>
13a3b59b15SWenyou Yang #include <asm/arch/clk.h>
14a3b59b15SWenyou Yang 
15a3b59b15SWenyou Yang #define ATMEL_SDHC_MIN_FREQ	400000
16a3b59b15SWenyou Yang 
17a0d0d86fSWenyou Yang #ifndef CONFIG_DM_MMC
atmel_sdhci_init(void * regbase,u32 id)18a3b59b15SWenyou Yang int atmel_sdhci_init(void *regbase, u32 id)
19a3b59b15SWenyou Yang {
20a3b59b15SWenyou Yang 	struct sdhci_host *host;
21a3b59b15SWenyou Yang 	u32 max_clk, min_clk = ATMEL_SDHC_MIN_FREQ;
22a3b59b15SWenyou Yang 
23a3b59b15SWenyou Yang 	host = (struct sdhci_host *)calloc(1, sizeof(struct sdhci_host));
24a3b59b15SWenyou Yang 	if (!host) {
25a3b59b15SWenyou Yang 		printf("%s: sdhci_host calloc failed\n", __func__);
26a3b59b15SWenyou Yang 		return -ENOMEM;
27a3b59b15SWenyou Yang 	}
28a3b59b15SWenyou Yang 
29a3b59b15SWenyou Yang 	host->name = "atmel_sdhci";
30a3b59b15SWenyou Yang 	host->ioaddr = regbase;
31b3125088SWenyou Yang 	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
32a3b59b15SWenyou Yang 	max_clk = at91_get_periph_generated_clk(id);
33a3b59b15SWenyou Yang 	if (!max_clk) {
34a3b59b15SWenyou Yang 		printf("%s: Failed to get the proper clock\n", __func__);
35a3b59b15SWenyou Yang 		free(host);
36a3b59b15SWenyou Yang 		return -ENODEV;
37a3b59b15SWenyou Yang 	}
386d0e34bfSStefan Herbrechtsmeier 	host->max_clk = max_clk;
39a3b59b15SWenyou Yang 
406d0e34bfSStefan Herbrechtsmeier 	add_sdhci(host, 0, min_clk);
41a3b59b15SWenyou Yang 
42a3b59b15SWenyou Yang 	return 0;
43a3b59b15SWenyou Yang }
44a0d0d86fSWenyou Yang 
45a0d0d86fSWenyou Yang #else
46a0d0d86fSWenyou Yang 
47a0d0d86fSWenyou Yang DECLARE_GLOBAL_DATA_PTR;
48a0d0d86fSWenyou Yang 
49a0d0d86fSWenyou Yang struct atmel_sdhci_plat {
50a0d0d86fSWenyou Yang 	struct mmc_config cfg;
51a0d0d86fSWenyou Yang 	struct mmc mmc;
52a0d0d86fSWenyou Yang };
53a0d0d86fSWenyou Yang 
atmel_sdhci_probe(struct udevice * dev)54a0d0d86fSWenyou Yang static int atmel_sdhci_probe(struct udevice *dev)
55a0d0d86fSWenyou Yang {
56a0d0d86fSWenyou Yang 	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
57a0d0d86fSWenyou Yang 	struct atmel_sdhci_plat *plat = dev_get_platdata(dev);
58a0d0d86fSWenyou Yang 	struct sdhci_host *host = dev_get_priv(dev);
59a0d0d86fSWenyou Yang 	u32 max_clk;
60a0d0d86fSWenyou Yang 	u32 caps, caps_1;
61a0d0d86fSWenyou Yang 	u32 clk_base, clk_mul;
62a0d0d86fSWenyou Yang 	ulong gck_rate;
63a0d0d86fSWenyou Yang 	struct clk clk;
64a0d0d86fSWenyou Yang 	int ret;
65a0d0d86fSWenyou Yang 
66339cb073SWenyou Yang 	ret = clk_get_by_index(dev, 0, &clk);
67a0d0d86fSWenyou Yang 	if (ret)
68a0d0d86fSWenyou Yang 		return ret;
69a0d0d86fSWenyou Yang 
70a0d0d86fSWenyou Yang 	ret = clk_enable(&clk);
71a0d0d86fSWenyou Yang 	if (ret)
72a0d0d86fSWenyou Yang 		return ret;
73a0d0d86fSWenyou Yang 
74a0d0d86fSWenyou Yang 	host->name = dev->name;
75*a821c4afSSimon Glass 	host->ioaddr = (void *)devfdt_get_addr(dev);
76a0d0d86fSWenyou Yang 
77b3125088SWenyou Yang 	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
78e160f7d4SSimon Glass 	host->bus_width	= fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
79a0d0d86fSWenyou Yang 					 "bus-width", 4);
80a0d0d86fSWenyou Yang 
81a0d0d86fSWenyou Yang 	caps = sdhci_readl(host, SDHCI_CAPABILITIES);
82a0d0d86fSWenyou Yang 	clk_base = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
83a0d0d86fSWenyou Yang 	caps_1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
84a0d0d86fSWenyou Yang 	clk_mul = (caps_1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;
85a0d0d86fSWenyou Yang 	gck_rate = clk_base * 1000000 * (clk_mul + 1);
86a0d0d86fSWenyou Yang 
87339cb073SWenyou Yang 	ret = clk_get_by_index(dev, 1, &clk);
88a0d0d86fSWenyou Yang 	if (ret)
89a0d0d86fSWenyou Yang 		return ret;
90a0d0d86fSWenyou Yang 
91a0d0d86fSWenyou Yang 	ret = clk_set_rate(&clk, gck_rate);
92a0d0d86fSWenyou Yang 	if (ret)
93a0d0d86fSWenyou Yang 		return ret;
94a0d0d86fSWenyou Yang 
95a0d0d86fSWenyou Yang 	max_clk = clk_get_rate(&clk);
96a0d0d86fSWenyou Yang 	if (!max_clk)
97a0d0d86fSWenyou Yang 		return -EINVAL;
98a0d0d86fSWenyou Yang 
996d0e34bfSStefan Herbrechtsmeier 	host->max_clk = max_clk;
1006d0e34bfSStefan Herbrechtsmeier 
1016d0e34bfSStefan Herbrechtsmeier 	ret = sdhci_setup_cfg(&plat->cfg, host, 0, ATMEL_SDHC_MIN_FREQ);
102a0d0d86fSWenyou Yang 	if (ret)
103a0d0d86fSWenyou Yang 		return ret;
104a0d0d86fSWenyou Yang 
105a0d0d86fSWenyou Yang 	host->mmc = &plat->mmc;
106a0d0d86fSWenyou Yang 	host->mmc->dev = dev;
107a0d0d86fSWenyou Yang 	host->mmc->priv = host;
108a0d0d86fSWenyou Yang 	upriv->mmc = host->mmc;
109a0d0d86fSWenyou Yang 
110a0d0d86fSWenyou Yang 	clk_free(&clk);
111a0d0d86fSWenyou Yang 
112a0d0d86fSWenyou Yang 	return sdhci_probe(dev);
113a0d0d86fSWenyou Yang }
114a0d0d86fSWenyou Yang 
atmel_sdhci_bind(struct udevice * dev)115a0d0d86fSWenyou Yang static int atmel_sdhci_bind(struct udevice *dev)
116a0d0d86fSWenyou Yang {
117a0d0d86fSWenyou Yang 	struct atmel_sdhci_plat *plat = dev_get_platdata(dev);
118a0d0d86fSWenyou Yang 
11924f5aec3SMasahiro Yamada 	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
120a0d0d86fSWenyou Yang }
121a0d0d86fSWenyou Yang 
122a0d0d86fSWenyou Yang static const struct udevice_id atmel_sdhci_ids[] = {
123a0d0d86fSWenyou Yang 	{ .compatible = "atmel,sama5d2-sdhci" },
124a0d0d86fSWenyou Yang 	{ }
125a0d0d86fSWenyou Yang };
126a0d0d86fSWenyou Yang 
127a0d0d86fSWenyou Yang U_BOOT_DRIVER(atmel_sdhci_drv) = {
128a0d0d86fSWenyou Yang 	.name		= "atmel_sdhci",
129a0d0d86fSWenyou Yang 	.id		= UCLASS_MMC,
130a0d0d86fSWenyou Yang 	.of_match	= atmel_sdhci_ids,
131a0d0d86fSWenyou Yang 	.ops		= &sdhci_ops,
132a0d0d86fSWenyou Yang 	.bind		= atmel_sdhci_bind,
133a0d0d86fSWenyou Yang 	.probe		= atmel_sdhci_probe,
134a0d0d86fSWenyou Yang 	.priv_auto_alloc_size = sizeof(struct sdhci_host),
135a0d0d86fSWenyou Yang 	.platdata_auto_alloc_size = sizeof(struct atmel_sdhci_plat),
136a0d0d86fSWenyou Yang };
137a0d0d86fSWenyou Yang #endif
138