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
|
/*
* Freescale eSDHC controller driver generics for OF and pltfm.
*
* Copyright (C) 2007, 2011, 2012 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc.
* Copyright (c) 2010 Pengutronix e.K.
* Author: Wolfram Sang <w.sang@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*/
#ifndef _DRIVERS_MMC_SDHCI_ESDHC_H
#define _DRIVERS_MMC_SDHCI_ESDHC_H
#include <linux/platform_device.h>
/*
* Ops and quirks for the Freescale eSDHC controller.
*/
#define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \
SDHCI_QUIRK_NO_BUSY_IRQ | \
SDHCI_QUIRK_NONSTANDARD_CLOCK | \
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \
SDHCI_QUIRK_PIO_NEEDS_DELAY | \
SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
#define ESDHC_SYSTEM_CONTROL 0x2c
#define ESDHC_CLOCK_MASK 0x0000fff0
#define ESDHC_PREDIV_SHIFT 8
#define ESDHC_DIVIDER_SHIFT 4
#define ESDHC_CLOCK_PEREN 0x00000004
#define ESDHC_CLOCK_HCKEN 0x00000002
#define ESDHC_CLOCK_IPGEN 0x00000001
/* pltfm-specific */
#define ESDHC_HOST_CONTROL_LE 0x20
/* OF-specific */
#define ESDHC_DMA_SYSCTL 0x40c
#define ESDHC_DMA_SNOOP 0x00000040
#define ESDHC_HOST_CONTROL_RES 0x05
#define SDHCI_MIX_CTRL 0x48
#define SDHCI_MIX_CTRL_DDREN (1 << 3)
static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
{
int pre_div = 2;
int div = 1;
u32 temp;
struct esdhc_platform_data *boarddata;
int ddr_mode = 0;
struct platform_device *pdev = to_platform_device(host->mmc->parent);
boarddata = host->mmc->parent->platform_data;
if (cpu_is_mx6q() || cpu_is_mx6dl() || cpu_is_mx6sl()) {
pre_div = 1;
if (readl(host->ioaddr + SDHCI_MIX_CTRL) &
SDHCI_MIX_CTRL_DDREN) {
ddr_mode = 1;
pre_div = 2;
}
}
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
if (clock == 0)
goto out;
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2;
while (host->max_clk / pre_div / div > clock && div < 16)
div++;
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
clock, host->max_clk / pre_div / div);
pre_div >>= (1 + ddr_mode);
div--;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| (div << ESDHC_DIVIDER_SHIFT)
| (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
mdelay(1);
/* if there's board callback function
* for pad setting change, that means
* board needs to reconfig its pad for
* corresponding sd bus frequency
*/
if (boarddata->platform_pad_change) {
BUG_ON(!pdev);
boarddata->platform_pad_change(pdev->id, clock);
}
out:
host->clock = clock;
}
#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */
|