1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* (C) Copyright 2022 - Analog Devices, Inc.
*
* Written and/or maintained by Timesys Corporation
*
* Contact: Nathan Barrett-Morrison <nathan.morrison@timesys.com>
* Contact: Greg Malysa <greg.malysa@timesys.com>
*
* Based on Rockchip's sdhci.c file
*/
#include <clk.h>
#include <dm.h>
#include <malloc.h>
#include <sdhci.h>
#include <asm/cache.h>
/* 400KHz is max freq for card ID etc. Use that as min */
#define EMMC_MIN_FREQ 400000
/* Check if an operation crossed a boundary of size ADMA_BOUNDARY_ALIGN */
#define ADMA_BOUNDARY_ALGN SZ_128M
#define BOUNDARY_OK(addr, len) \
(((addr) | (ADMA_BOUNDARY_ALGN - 1)) == (((addr) + (len) - 1) | \
(ADMA_BOUNDARY_ALGN - 1)))
/* We split a descriptor for every crossing of the ADMA alignment boundary,
* so we need an additional descriptor for every expected crossing.
* As I understand it, the max expected transaction size is:
* CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN
*
* With the way the SDHCI-ADMA driver is implemented, if ADMA_MAX_LEN was a
* clean power of two, we'd only ever need +1 descriptor as the first
* descriptor that got split would then bring the remaining DMA
* destination addresses into alignment. Unfortunately, it's currently
* hardcoded to a non-power-of-two value.
*
* If that ever becomes parameterized, ADMA max length can be set to
* 0x10000, and set this to 1.
*/
#define ADMA_POTENTIAL_CROSSINGS \
DIV_ROUND_UP((CONFIG_SYS_MMC_MAX_BLK_COUNT * MMC_MAX_BLOCK_LEN), \
ADMA_BOUNDARY_ALGN)
/* +1 descriptor for each crossing.
*/
#define ADMA_TABLE_EXTRA_SZ (ADMA_POTENTIAL_CROSSINGS * ADMA_DESC_LEN)
struct adi_sdhc_plat {
struct mmc_config cfg;
struct mmc mmc;
};
void adi_dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, bool end)
{
int tmplen, offset;
if (likely(!len || BOUNDARY_OK(addr, len))) {
sdhci_adma_write_desc(host, desc, addr, len, end);
return;
}
offset = addr & (ADMA_BOUNDARY_ALGN - 1);
tmplen = ADMA_BOUNDARY_ALGN - offset;
sdhci_adma_write_desc(host, desc, addr, tmplen, false);
addr += tmplen;
len -= tmplen;
sdhci_adma_write_desc(host, desc, addr, len, end);
}
struct sdhci_ops adi_dwcmshc_sdhci_ops = {
.adma_write_desc = adi_dwcmshc_adma_write_desc,
};
static int adi_dwcmshc_sdhci_probe(struct udevice *dev)
{
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct adi_sdhc_plat *plat = dev_get_plat(dev);
struct sdhci_host *host = dev_get_priv(dev);
int max_frequency, ret;
struct clk clk;
max_frequency = dev_read_u32_default(dev, "max-frequency", 0);
ret = clk_get_by_index(dev, 0, &clk);
host->quirks = 0;
host->max_clk = max_frequency;
/*
* The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg
* doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't
* check for other bus-width values.
*/
if (host->bus_width == 8)
host->host_caps |= MMC_MODE_8BIT;
host->mmc = &plat->mmc;
host->mmc->priv = host;
host->mmc->dev = dev;
upriv->mmc = host->mmc;
host->ops = &adi_dwcmshc_sdhci_ops;
host->adma_desc_table = memalign(ARCH_DMA_MINALIGN,
ADMA_TABLE_SZ + ADMA_TABLE_EXTRA_SZ);
host->adma_addr = virt_to_phys(host->adma_desc_table);
ret = sdhci_setup_cfg(&plat->cfg, host, 0, EMMC_MIN_FREQ);
if (ret)
return ret;
return sdhci_probe(dev);
}
static int adi_dwcmshc_sdhci_of_to_plat(struct udevice *dev)
{
struct sdhci_host *host = dev_get_priv(dev);
host->name = dev->name;
host->ioaddr = dev_read_addr_ptr(dev);
host->bus_width = dev_read_u32_default(dev, "bus-width", 4);
return 0;
}
static int adi_sdhci_bind(struct udevice *dev)
{
struct adi_sdhc_plat *plat = dev_get_plat(dev);
return sdhci_bind(dev, &plat->mmc, &plat->cfg);
}
static const struct udevice_id adi_dwcmshc_sdhci_ids[] = {
{ .compatible = "adi,dwc-sdhci" },
{ }
};
U_BOOT_DRIVER(adi_dwcmshc_sdhci_drv) = {
.name = "adi_sdhci",
.id = UCLASS_MMC,
.of_match = adi_dwcmshc_sdhci_ids,
.of_to_plat = adi_dwcmshc_sdhci_of_to_plat,
.ops = &sdhci_ops,
.bind = adi_sdhci_bind,
.probe = adi_dwcmshc_sdhci_probe,
.priv_auto = sizeof(struct sdhci_host),
.plat_auto = sizeof(struct adi_sdhc_plat),
};
|