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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
From ee83ea40a3a1d1cb6447fdd345384d9cabf21c83 Mon Sep 17 00:00:00 2001
From: Andy Green <andy@openmoko.com>
Date: Fri, 25 Jul 2008 23:06:20 +0100
Subject: [PATCH] fix-force-sdcard-clk-off-when-idle.patch
Existing Glamo bit for stopping SD Card Clock when there is no
transfer taking place does not work. This patch adds stuff around
the transfer code to force the SD clock up when something is going on
and down when it is idle. This'll save a little power and noise ;-)
I tested it briefly and was able to SD Boot normally on Sandisk 512M.
Wider testing is appreciated.
Signed-off-by: Andy Green <andy@openmoko.com>
---
drivers/mfd/glamo/glamo-mci.c | 126 +++++++++++++++++++++++++++++------------
1 files changed, 89 insertions(+), 37 deletions(-)
diff --git a/drivers/mfd/glamo/glamo-mci.c b/drivers/mfd/glamo/glamo-mci.c
index 37e3d3c..b53827e 100644
--- a/drivers/mfd/glamo/glamo-mci.c
+++ b/drivers/mfd/glamo/glamo-mci.c
@@ -20,6 +20,7 @@
#include <linux/pcf50633.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/spinlock.h>
#include <asm/dma.h>
#include <asm/dma-mapping.h>
@@ -32,6 +33,7 @@
/* from glamo-core.c */
extern struct glamo_mci_pdata glamo_mci_def_pdata;
+static spinlock_t clock_lock;
#define DRIVER_NAME "glamo-mci"
#define RESSIZE(ressource) (((ressource)->end - (ressource)->start) + 1)
@@ -164,6 +166,67 @@ static int do_pio_write(struct glamo_mci_host *host)
return err;
}
+static void __glamo_mci_fix_card_div(struct glamo_mci_host *host, int div)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&clock_lock, flags);
+
+ if (div < 0) {
+ /* stop clock - remove clock from divider input */
+ writew(readw(glamo_mci_def_pdata.pglamo->base +
+ GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK),
+ glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
+ } else {
+ /* set the nearest prescaler factor
+ *
+ * register shared with SCLK divisor -- no chance of race because
+ * we don't use sensor interface
+ */
+ writew_dly((readw(glamo_mci_def_pdata.pglamo->base +
+ GLAMO_REG_CLOCK_GEN8) & 0xff00) | div,
+ glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8);
+ /* enable clock to divider input */
+ writew_dly(readw(glamo_mci_def_pdata.pglamo->base +
+ GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
+ glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
+ }
+
+ spin_unlock_irqrestore(&clock_lock, flags);
+}
+
+static int __glamo_mci_set_card_clock(struct glamo_mci_host *host, int freq,
+ int *division)
+{
+ int div = 0;
+ int real_rate = 0;
+
+ if (freq) {
+ /* Set clock */
+ for (div = 0; div < 256; div++) {
+ real_rate = host->clk_rate / (div + 1);
+ if (real_rate <= freq)
+ break;
+ }
+ if (div > 255)
+ div = 255;
+
+ if (division)
+ *division = div;
+
+ __glamo_mci_fix_card_div(host, div);
+
+ } else {
+ /* stop clock */
+ if (division)
+ *division = 0xff;
+
+ __glamo_mci_fix_card_div(host, -1); /* clock off */
+ }
+
+ return real_rate;
+}
+
static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc)
{
struct glamo_mci_host *host = (struct glamo_mci_host *)
@@ -212,6 +275,10 @@ static void glamo_mci_irq(unsigned int irq, struct irq_desc *desc)
glamo_mci_send_request(host->mmc);
host->cmd_is_stop = 0;
}
+
+ /* clock off */
+ __glamo_mci_fix_card_div(host, -1);
+
done:
host->complete_what = COMPLETION_NONE;
host->mrq = NULL;
@@ -441,8 +508,11 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
cmd->opcode, cmd->arg, cmd->data, cmd->mrq->stop,
cmd->flags);
+ /* resume requested clock rate */
+ __glamo_mci_fix_card_div(host, host->clk_div);
+
if (glamo_mci_send_command(host, cmd))
- return;
+ goto bail;
/*
* we must spin until response is ready or timed out
* -- we don't get interrupts unless there is a bulk rx
@@ -464,7 +534,7 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
cmd->error = -EILSEQ;
if (host->cmd_is_stop)
- return;
+ goto bail;
if (cmd->error) {
dev_err(&host->pdev->dev, "Error after cmd: 0x%x\n", status);
@@ -516,10 +586,12 @@ static void glamo_mci_send_request(struct mmc_host *mmc)
if (cmd->data->error)
cmd->data->error = -ETIMEDOUT;
dev_err(&host->pdev->dev, "Payload timeout\n");
- return;
+ goto bail;
}
- /* yay we are an interrupt controller! -- call the ISR */
+ /* yay we are an interrupt controller! -- call the ISR
+ * it will stop clock to card
+ */
glamo_mci_irq(IRQ_GLAMO(GLAMO_IRQIDX_MMC),
irq_desc + IRQ_GLAMO(GLAMO_IRQIDX_MMC));
}
@@ -529,6 +601,12 @@ done:
host->complete_what = COMPLETION_NONE;
host->mrq = NULL;
mmc_request_done(host->mmc, cmd->mrq);
+ return;
+
+bail:
+ /* stop the clock to card */
+ __glamo_mci_fix_card_div(host, -1);
+ return;
}
static void glamo_mci_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -556,11 +634,12 @@ static void glamo_mci_reset(struct glamo_mci_host *host)
glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_MMC);
}
+
static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct glamo_mci_host *host = mmc_priv(mmc);
- int mci_psc = 0;
int n = 0;
+ int div;
/* Set power */
switch(ios->power_mode) {
@@ -590,43 +669,15 @@ static void glamo_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
host->power_mode_current = ios->power_mode;
- /* Set clock */
-/* if (ios->clock) { */
- for (mci_psc = 0; mci_psc < 256; mci_psc++) {
- host->real_rate = host->clk_rate / (mci_psc + 1);
- if (host->real_rate <= ios->clock)
- break;
- }
- if (mci_psc > 255)
- mci_psc = 255;
- host->clk_div = mci_psc;
- /* set the nearest prescaler factor
- *
- * register shared with SCLK divisor -- no chance of race because
- * we don't use sensor interface
- */
- writew_dly((readw(glamo_mci_def_pdata.pglamo->base +
- GLAMO_REG_CLOCK_GEN8) & 0xff00) | host->clk_div,
- glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN8);
- /* enable clock to divider input */
- writew_dly(readw(glamo_mci_def_pdata.pglamo->base +
- GLAMO_REG_CLOCK_GEN5_1) | GLAMO_CLOCK_GEN51_EN_DIV_TCLK,
- glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
-#if 0
- } else { /* stop clock */
- host->real_rate = 0;
- /* remove clock from divider input */
- writew(readw(glamo_mci_def_pdata.pglamo->base +
- GLAMO_REG_CLOCK_GEN5_1) & (~GLAMO_CLOCK_GEN51_EN_DIV_TCLK),
- glamo_mci_def_pdata.pglamo->base + GLAMO_REG_CLOCK_GEN5_1);
- }
-#endif
+ host->real_rate = __glamo_mci_set_card_clock(host, ios->clock, &div);
+ host->clk_div = div;
+
if ((ios->power_mode == MMC_POWER_ON) ||
(ios->power_mode == MMC_POWER_UP)) {
dev_info(&host->pdev->dev,
"powered (vdd = %d) clk: %lukHz div=%d (req: %ukHz). "
"Bus width=%d\n",ios->vdd,
- host->real_rate / 1000, mci_psc,
+ host->real_rate / 1000, host->real_rate,
ios->clock / 1000, ios->bus_width);
} else
dev_info(&host->pdev->dev, "glamo_mci_set_ios: power down.\n");
@@ -856,6 +907,7 @@ static struct platform_driver glamo_mci_driver =
static int __init glamo_mci_init(void)
{
+ spin_lock_init(&clock_lock);
platform_driver_register(&glamo_mci_driver);
return 0;
}
--
1.5.6.3
|