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