blob: 9af6b971984483d29bfea863e91149cbb914d65f [file] [log] [blame]
Kongyang Liu331ee412024-03-10 01:51:55 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2024, Kongyang Liu <seashell11234455@gmail.com>
4 */
5
6#include <dm.h>
7#include <mmc.h>
8#include <sdhci.h>
9#include <linux/delay.h>
10
11#define SDHCI_PHY_TX_RX_DLY 0x240
12#define MMC_MAX_CLOCK 375000000
13#define TUNE_MAX_PHCODE 128
14
15struct cv1800b_sdhci_plat {
16 struct mmc_config cfg;
17 struct mmc mmc;
18};
19
20static void cv1800b_set_tap_delay(struct sdhci_host *host, u16 tap)
21{
22 sdhci_writel(host, tap << 16, SDHCI_PHY_TX_RX_DLY);
23}
24
25static void cv1800b_sdhci_reset(struct sdhci_host *host, u8 mask)
26{
27 sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
28 while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask)
29 udelay(10);
30}
31
32static int cv1800b_execute_tuning(struct mmc *mmc, u8 opcode)
33{
34 struct sdhci_host *host = dev_get_priv(mmc->dev);
35
36 u16 tap;
37
38 int current_size = 0;
39 int max_size = 0;
40 int max_window = 0;
41
42 for (tap = 0; tap < TUNE_MAX_PHCODE; tap++) {
43 cv1800b_set_tap_delay(host, tap);
44
Jaehoon Chung34a3b962024-04-15 16:56:50 +090045 if (mmc_send_tuning(host->mmc, opcode)) {
Kongyang Liu331ee412024-03-10 01:51:55 +080046 current_size = 0;
47 } else {
48 current_size++;
49 if (current_size > max_size) {
50 max_size = current_size;
51 max_window = tap;
52 }
53 }
54 }
55
56 cv1800b_sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
57
58 cv1800b_set_tap_delay(host, max_window - max_size / 2);
59
60 return 0;
61}
62
63const struct sdhci_ops cv1800b_sdhci_sd_ops = {
64 .platform_execute_tuning = cv1800b_execute_tuning,
65};
66
67static int cv1800b_sdhci_bind(struct udevice *dev)
68{
69 struct cv1800b_sdhci_plat *plat = dev_get_plat(dev);
70
71 return sdhci_bind(dev, &plat->mmc, &plat->cfg);
72}
73
74static int cv1800b_sdhci_probe(struct udevice *dev)
75{
76 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
77 struct cv1800b_sdhci_plat *plat = dev_get_plat(dev);
78 struct sdhci_host *host = dev_get_priv(dev);
79 int ret;
80
81 host->name = dev->name;
82 host->ioaddr = devfdt_get_addr_ptr(dev);
83
84 upriv->mmc = &plat->mmc;
85 host->mmc = &plat->mmc;
86 host->mmc->priv = host;
87 host->mmc->dev = dev;
88 host->ops = &cv1800b_sdhci_sd_ops;
89 host->max_clk = MMC_MAX_CLOCK;
90
91 ret = mmc_of_parse(dev, &plat->cfg);
92 if (ret)
93 return ret;
94
95 ret = sdhci_setup_cfg(&plat->cfg, host, 0, 200000);
96 if (ret)
97 return ret;
98
99 return sdhci_probe(dev);
100}
101
102static const struct udevice_id cv1800b_sdhci_match[] = {
103 { .compatible = "sophgo,cv1800b-dwcmshc" },
104 { }
105};
106
107U_BOOT_DRIVER(cv1800b_sdhci) = {
108 .name = "sdhci-cv1800b",
109 .id = UCLASS_MMC,
110 .of_match = cv1800b_sdhci_match,
111 .bind = cv1800b_sdhci_bind,
112 .probe = cv1800b_sdhci_probe,
113 .priv_auto = sizeof(struct sdhci_host),
114 .plat_auto = sizeof(struct cv1800b_sdhci_plat),
115 .ops = &sdhci_ops,
116};