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
|
/*
* (C) Copyright 2013 Altera Corporation <www.altera.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <fdtdec.h>
#include <libfdt.h>
#include <dwmmc.h>
#include <errno.h>
#include <asm/arch/dwmmc.h>
#include <asm/arch/clock_manager.h>
#include <asm/arch/system_manager.h>
static const struct socfpga_clock_manager *clock_manager_base =
(void *)SOCFPGA_CLKMGR_ADDRESS;
static const struct socfpga_system_manager *system_manager_base =
(void *)SOCFPGA_SYSMGR_ADDRESS;
/* socfpga implmentation specific drver private data */
struct dwmci_socfpga_priv_data {
unsigned int drvsel;
unsigned int smplsel;
};
static void socfpga_dwmci_clksel(struct dwmci_host *host)
{
struct dwmci_socfpga_priv_data *priv = host->priv;
/* Disable SDMMC clock. */
clrbits_le32(&clock_manager_base->per_pll.en,
CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK);
debug("%s: drvsel %d smplsel %d\n", __func__,
priv->drvsel, priv->smplsel);
writel(SYSMGR_SDMMC_CTRL_SET(priv->smplsel, priv->drvsel),
&system_manager_base->sdmmcgrp_ctrl);
debug("%s: SYSMGR_SDMMCGRP_CTRL_REG = 0x%x\n", __func__,
readl(&system_manager_base->sdmmcgrp_ctrl));
/* Enable SDMMC clock */
setbits_le32(&clock_manager_base->per_pll.en,
CLKMGR_PERPLLGRP_EN_SDMMCCLK_MASK);
}
static int socfpga_dwmci_of_probe(const void *blob, int node, const int idx)
{
/* FIXME: probe from DT eventually too/ */
const unsigned long clk = cm_get_mmc_controller_clk_hz();
struct dwmci_host *host;
struct dwmci_socfpga_priv_data *priv;
fdt_addr_t reg_base;
int bus_width, fifo_depth;
if (clk == 0) {
printf("DWMMC%d: MMC clock is zero!", idx);
return -EINVAL;
}
/* Get the register address from the device node */
reg_base = fdtdec_get_addr(blob, node, "reg");
if (!reg_base) {
printf("DWMMC%d: Can't get base address\n", idx);
return -EINVAL;
}
/* Get the bus width from the device node */
bus_width = fdtdec_get_int(blob, node, "bus-width", 0);
if (bus_width <= 0) {
printf("DWMMC%d: Can't get bus-width\n", idx);
return -EINVAL;
}
fifo_depth = fdtdec_get_int(blob, node, "fifo-depth", 0);
if (fifo_depth < 0) {
printf("DWMMC%d: Can't get FIFO depth\n", idx);
return -EINVAL;
}
/* Allocate the host */
host = calloc(1, sizeof(*host));
if (!host)
return -ENOMEM;
/* Allocate the priv */
priv = calloc(1, sizeof(*priv));
if (!priv) {
free(host);
return -ENOMEM;
}
host->name = "SOCFPGA DWMMC";
host->ioaddr = (void *)reg_base;
host->buswidth = bus_width;
host->clksel = socfpga_dwmci_clksel;
host->dev_index = idx;
/* Fixed clock divide by 4 which due to the SDMMC wrapper */
host->bus_hz = clk;
host->fifoth_val = MSIZE(0x2) |
RX_WMARK(fifo_depth / 2 - 1) | TX_WMARK(fifo_depth / 2);
priv->drvsel = fdtdec_get_uint(blob, node, "drvsel", 3);
priv->smplsel = fdtdec_get_uint(blob, node, "smplsel", 0);
host->priv = priv;
return add_dwmci(host, host->bus_hz, 400000);
}
static int socfpga_dwmci_process_node(const void *blob, int nodes[],
int count)
{
int i, node, ret;
for (i = 0; i < count; i++) {
node = nodes[i];
if (node <= 0)
continue;
ret = socfpga_dwmci_of_probe(blob, node, i);
if (ret) {
printf("%s: failed to decode dev %d\n", __func__, i);
return ret;
}
}
return 0;
}
int socfpga_dwmmc_init(const void *blob)
{
int nodes[2]; /* Max. two controllers. */
int ret, count;
count = fdtdec_find_aliases_for_id(blob, "mmc",
COMPAT_ALTERA_SOCFPGA_DWMMC,
nodes, ARRAY_SIZE(nodes));
ret = socfpga_dwmci_process_node(blob, nodes, count);
return ret;
}
|