summaryrefslogtreecommitdiff
path: root/drivers/mmc/mv_sdhci.c
blob: 556dd380466b4dedffd731973e384aca814a13fd (plain)
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
149
150
151
152
153
154
155
156
157
// SPDX-License-Identifier: GPL-2.0+
/*
 * Marvell SD Host Controller Interface
 */

#include <common.h>
#include <dm.h>
#include <malloc.h>
#include <sdhci.h>
#include <linux/mbus.h>

#define MVSDH_NAME "mv_sdh"

#define SDHCI_WINDOW_CTRL(win)		(0x4080 + ((win) << 4))
#define SDHCI_WINDOW_BASE(win)		(0x4084 + ((win) << 4))

static void sdhci_mvebu_mbus_config(void __iomem *base)
{
	const struct mbus_dram_target_info *dram;
	int i;

	dram = mvebu_mbus_dram_info();

	for (i = 0; i < 4; i++) {
		writel(0, base + SDHCI_WINDOW_CTRL(i));
		writel(0, base + SDHCI_WINDOW_BASE(i));
	}

	for (i = 0; i < dram->num_cs; i++) {
		const struct mbus_dram_window *cs = dram->cs + i;

		/* Write size, attributes and target id to control register */
		writel(((cs->size - 1) & 0xffff0000) | (cs->mbus_attr << 8) |
		       (dram->mbus_dram_target_id << 4) | 1,
		       base + SDHCI_WINDOW_CTRL(i));

		/* Write base address to base register */
		writel(cs->base, base + SDHCI_WINDOW_BASE(i));
	}
}

#ifndef CONFIG_DM_MMC

#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
static struct sdhci_ops mv_ops;

#if defined(CONFIG_SHEEVA_88SV331xV5)
#define SD_CE_ATA_2	0xEA
#define  MMC_CARD	0x1000
#define  MMC_WIDTH	0x0100
static inline void mv_sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
	struct mmc *mmc = host->mmc;
	u32 ata = (unsigned long)host->ioaddr + SD_CE_ATA_2;

	if (!IS_SD(mmc) && reg == SDHCI_HOST_CONTROL) {
		if (mmc->bus_width == 8)
			writew(readw(ata) | (MMC_CARD | MMC_WIDTH), ata);
		else
			writew(readw(ata) & ~(MMC_CARD | MMC_WIDTH), ata);
	}

	writeb(val, host->ioaddr + reg);
}

#else
#define mv_sdhci_writeb	NULL
#endif /* CONFIG_SHEEVA_88SV331xV5 */
#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */

int mv_sdh_init(unsigned long regbase, u32 max_clk, u32 min_clk, u32 quirks)
{
	struct sdhci_host *host = NULL;
	host = calloc(1, sizeof(*host));
	if (!host) {
		printf("sdh_host malloc fail!\n");
		return -ENOMEM;
	}

	host->name = MVSDH_NAME;
	host->ioaddr = (void *)regbase;
	host->quirks = quirks;
	host->max_clk = max_clk;
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
	memset(&mv_ops, 0, sizeof(struct sdhci_ops));
	mv_ops.write_b = mv_sdhci_writeb;
	host->ops = &mv_ops;
#endif

	if (CONFIG_IS_ENABLED(ARCH_MVEBU)) {
		/* Configure SDHCI MBUS mbus bridge windows */
		sdhci_mvebu_mbus_config((void __iomem *)regbase);
	}

	return add_sdhci(host, 0, min_clk);
}

#else

DECLARE_GLOBAL_DATA_PTR;

struct mv_sdhci_plat {
	struct mmc_config cfg;
	struct mmc mmc;
};

static int mv_sdhci_probe(struct udevice *dev)
{
	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
	struct mv_sdhci_plat *plat = dev_get_plat(dev);
	struct sdhci_host *host = dev_get_priv(dev);
	int ret;

	host->name = MVSDH_NAME;
	host->ioaddr = dev_read_addr_ptr(dev);
	host->quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_WAIT_SEND_CMD;
	host->mmc = &plat->mmc;
	host->mmc->dev = dev;
	host->mmc->priv = host;

	ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
	if (ret)
		return ret;

	if (CONFIG_IS_ENABLED(ARCH_MVEBU)) {
		/* Configure SDHCI MBUS mbus bridge windows */
		sdhci_mvebu_mbus_config(host->ioaddr);
	}

	upriv->mmc = host->mmc;

	return sdhci_probe(dev);
}

static int mv_sdhci_bind(struct udevice *dev)
{
	struct mv_sdhci_plat *plat = dev_get_plat(dev);

	return sdhci_bind(dev, &plat->mmc, &plat->cfg);
}

static const struct udevice_id mv_sdhci_ids[] = {
	{ .compatible = "marvell,armada-380-sdhci" },
	{ }
};

U_BOOT_DRIVER(mv_sdhci_drv) = {
	.name		= MVSDH_NAME,
	.id		= UCLASS_MMC,
	.of_match	= mv_sdhci_ids,
	.bind		= mv_sdhci_bind,
	.probe		= mv_sdhci_probe,
	.ops		= &sdhci_ops,
	.priv_auto	= sizeof(struct sdhci_host),
	.plat_auto	= sizeof(struct mv_sdhci_plat),
};
#endif /* CONFIG_DM_MMC */