xref: /openbmc/u-boot/drivers/mmc/atmel_sdhci.c (revision e8f80a5a)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2a3b59b15SWenyou Yang /*
3a3b59b15SWenyou Yang  * Copyright (C) 2015 Atmel Corporation
4a3b59b15SWenyou Yang  *		      Wenyou.Yang <wenyou.yang@atmel.com>
5a3b59b15SWenyou Yang  */
6a3b59b15SWenyou Yang 
7a3b59b15SWenyou Yang #include <common.h>
8a0d0d86fSWenyou Yang #include <clk.h>
9a0d0d86fSWenyou Yang #include <dm.h>
10a3b59b15SWenyou Yang #include <malloc.h>
11a3b59b15SWenyou Yang #include <sdhci.h>
12a3b59b15SWenyou Yang #include <asm/arch/clk.h>
13a3b59b15SWenyou Yang 
14a3b59b15SWenyou Yang #define ATMEL_SDHC_MIN_FREQ	400000
15327713a6SLudovic Desroches #define ATMEL_SDHC_GCK_RATE	240000000
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 	struct clk clk;
61a0d0d86fSWenyou Yang 	int ret;
62a0d0d86fSWenyou Yang 
63339cb073SWenyou Yang 	ret = clk_get_by_index(dev, 0, &clk);
64a0d0d86fSWenyou Yang 	if (ret)
65a0d0d86fSWenyou Yang 		return ret;
66a0d0d86fSWenyou Yang 
67a0d0d86fSWenyou Yang 	ret = clk_enable(&clk);
68a0d0d86fSWenyou Yang 	if (ret)
69a0d0d86fSWenyou Yang 		return ret;
70a0d0d86fSWenyou Yang 
71a0d0d86fSWenyou Yang 	host->name = dev->name;
72a821c4afSSimon Glass 	host->ioaddr = (void *)devfdt_get_addr(dev);
73a0d0d86fSWenyou Yang 
74b3125088SWenyou Yang 	host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD;
75e160f7d4SSimon Glass 	host->bus_width	= fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
76a0d0d86fSWenyou Yang 					 "bus-width", 4);
77a0d0d86fSWenyou Yang 
78339cb073SWenyou Yang 	ret = clk_get_by_index(dev, 1, &clk);
79a0d0d86fSWenyou Yang 	if (ret)
80a0d0d86fSWenyou Yang 		return ret;
81a0d0d86fSWenyou Yang 
82327713a6SLudovic Desroches 	ret = clk_set_rate(&clk, ATMEL_SDHC_GCK_RATE);
83a0d0d86fSWenyou Yang 	if (ret)
84a0d0d86fSWenyou Yang 		return ret;
85a0d0d86fSWenyou Yang 
86a0d0d86fSWenyou Yang 	max_clk = clk_get_rate(&clk);
87a0d0d86fSWenyou Yang 	if (!max_clk)
88a0d0d86fSWenyou Yang 		return -EINVAL;
89a0d0d86fSWenyou Yang 
906d0e34bfSStefan Herbrechtsmeier 	host->max_clk = max_clk;
916d0e34bfSStefan Herbrechtsmeier 
926d0e34bfSStefan Herbrechtsmeier 	ret = sdhci_setup_cfg(&plat->cfg, host, 0, ATMEL_SDHC_MIN_FREQ);
93a0d0d86fSWenyou Yang 	if (ret)
94a0d0d86fSWenyou Yang 		return ret;
95a0d0d86fSWenyou Yang 
96a0d0d86fSWenyou Yang 	host->mmc = &plat->mmc;
97a0d0d86fSWenyou Yang 	host->mmc->dev = dev;
98a0d0d86fSWenyou Yang 	host->mmc->priv = host;
99a0d0d86fSWenyou Yang 	upriv->mmc = host->mmc;
100a0d0d86fSWenyou Yang 
101a0d0d86fSWenyou Yang 	clk_free(&clk);
102a0d0d86fSWenyou Yang 
103a0d0d86fSWenyou Yang 	return sdhci_probe(dev);
104a0d0d86fSWenyou Yang }
105a0d0d86fSWenyou Yang 
atmel_sdhci_bind(struct udevice * dev)106a0d0d86fSWenyou Yang static int atmel_sdhci_bind(struct udevice *dev)
107a0d0d86fSWenyou Yang {
108a0d0d86fSWenyou Yang 	struct atmel_sdhci_plat *plat = dev_get_platdata(dev);
109a0d0d86fSWenyou Yang 
11024f5aec3SMasahiro Yamada 	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
111a0d0d86fSWenyou Yang }
112a0d0d86fSWenyou Yang 
113a0d0d86fSWenyou Yang static const struct udevice_id atmel_sdhci_ids[] = {
114a0d0d86fSWenyou Yang 	{ .compatible = "atmel,sama5d2-sdhci" },
115a0d0d86fSWenyou Yang 	{ }
116a0d0d86fSWenyou Yang };
117a0d0d86fSWenyou Yang 
118a0d0d86fSWenyou Yang U_BOOT_DRIVER(atmel_sdhci_drv) = {
119a0d0d86fSWenyou Yang 	.name		= "atmel_sdhci",
120a0d0d86fSWenyou Yang 	.id		= UCLASS_MMC,
121a0d0d86fSWenyou Yang 	.of_match	= atmel_sdhci_ids,
122a0d0d86fSWenyou Yang 	.ops		= &sdhci_ops,
123a0d0d86fSWenyou Yang 	.bind		= atmel_sdhci_bind,
124a0d0d86fSWenyou Yang 	.probe		= atmel_sdhci_probe,
125a0d0d86fSWenyou Yang 	.priv_auto_alloc_size = sizeof(struct sdhci_host),
126a0d0d86fSWenyou Yang 	.platdata_auto_alloc_size = sizeof(struct atmel_sdhci_plat),
127a0d0d86fSWenyou Yang };
128a0d0d86fSWenyou Yang #endif
129