summaryrefslogtreecommitdiff
path: root/drivers/mmc/kona_sdhci.c
blob: 2bbe673b91299d0d6d61b975cd43c44eb9e97f2f (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2013 Broadcom Corporation.
 */

#include <common.h>
#include <malloc.h>
#include <sdhci.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <asm/kona-common/clk.h>

#define SDHCI_CORECTRL_OFFSET		0x00008000
#define SDHCI_CORECTRL_EN		0x01
#define SDHCI_CORECTRL_RESET		0x02

#define SDHCI_CORESTAT_OFFSET		0x00008004
#define SDHCI_CORESTAT_CD_SW		0x01

#define SDHCI_COREIMR_OFFSET		0x00008008
#define SDHCI_COREIMR_IP		0x01

static int init_kona_mmc_core(struct sdhci_host *host)
{
	unsigned int mask;
	unsigned int timeout;

	if (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & SDHCI_RESET_ALL) {
		printf("%s: sd host controller reset error\n", __func__);
		return -EBUSY;
	}

	/* For kona a hardware reset before anything else. */
	mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET) | SDHCI_CORECTRL_RESET;
	sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET);

	/* Wait max 100 ms */
	timeout = 1000;
	do {
		if (timeout == 0) {
			printf("%s: reset timeout error\n", __func__);
			return -ETIMEDOUT;
		}
		timeout--;
		udelay(100);
	} while (0 ==
		 (sdhci_readl(host, SDHCI_CORECTRL_OFFSET) &
		  SDHCI_CORECTRL_RESET));

	/* Clear the reset bit. */
	mask = mask & ~SDHCI_CORECTRL_RESET;
	sdhci_writel(host, mask, SDHCI_CORECTRL_OFFSET);

	/* Enable AHB clock */
	mask = sdhci_readl(host, SDHCI_CORECTRL_OFFSET);
	sdhci_writel(host, mask | SDHCI_CORECTRL_EN, SDHCI_CORECTRL_OFFSET);

	/* Enable interrupts */
	sdhci_writel(host, SDHCI_COREIMR_IP, SDHCI_COREIMR_OFFSET);

	/* Make sure Card is detected in controller */
	mask = sdhci_readl(host, SDHCI_CORESTAT_OFFSET);
	sdhci_writel(host, mask | SDHCI_CORESTAT_CD_SW, SDHCI_CORESTAT_OFFSET);

	/* Wait max 100 ms */
	timeout = 1000;
	while (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
		if (timeout == 0) {
			printf("%s: CARD DETECT timeout error\n", __func__);
			return -ETIMEDOUT;
		}
		timeout--;
		udelay(100);
	}
	return 0;
}

int kona_sdhci_init(int dev_index, u32 min_clk, u32 quirks)
{
	int ret = 0;
	u32 max_clk;
	void *reg_base;
	struct sdhci_host *host = NULL;

	host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host));
	if (!host) {
		printf("%s: sdhci host malloc fail!\n", __func__);
		return -ENOMEM;
	}
	switch (dev_index) {
	case 0:
		reg_base = (void *)CONFIG_SYS_SDIO_BASE0;
		ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO0_MAX_CLK,
				      &max_clk);
		break;
	case 1:
		reg_base = (void *)CONFIG_SYS_SDIO_BASE1;
		ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO1_MAX_CLK,
				      &max_clk);
		break;
	case 2:
		reg_base = (void *)CONFIG_SYS_SDIO_BASE2;
		ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO2_MAX_CLK,
				      &max_clk);
		break;
	case 3:
		reg_base = (void *)CONFIG_SYS_SDIO_BASE3;
		ret = clk_sdio_enable(reg_base, CONFIG_SYS_SDIO3_MAX_CLK,
				      &max_clk);
		break;
	default:
		printf("%s: sdio dev index %d not supported\n",
		       __func__, dev_index);
		ret = -EINVAL;
	}
	if (ret) {
		free(host);
		return ret;
	}

	host->name = "kona-sdhci";
	host->ioaddr = reg_base;
	host->quirks = quirks;
	host->max_clk = max_clk;

	if (init_kona_mmc_core(host)) {
		free(host);
		return -EINVAL;
	}

	add_sdhci(host, 0, min_clk);
	return ret;
}