3c1c2492316749e9d88dfe20bc3d89290380e138
[openwrt.git] / target / linux / s3c24xx / patches-2.6.31 / 200-s3c-mci.patch
1 diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
2 index d84c880..3e79f43 100644
3 --- a/drivers/mmc/core/core.c
4 +++ b/drivers/mmc/core/core.c
5 @@ -59,10 +59,11 @@ static int mmc_schedule_delayed_work(struct delayed_work *work,
6  /*
7   * Internal function. Flush all scheduled work from the MMC work queue.
8   */
9 -static void mmc_flush_scheduled_work(void)
10 +void mmc_flush_scheduled_work(void)
11  {
12         flush_workqueue(workqueue);
13  }
14 +EXPORT_SYMBOL_GPL(mmc_flush_scheduled_work);
15  
16  /**
17   *     mmc_request_done - finish processing an MMC request
18 --- a/include/linux/mmc/core.h
19 +++ b/include/linux/mmc/core.h
20 @@ -129,6 +129,8 @@ struct mmc_request {
21  struct mmc_host;
22  struct mmc_card;
23  
24 +extern void mmc_flush_scheduled_work(void);
25 +
26  extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
27  extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
28  extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
29 diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
30 index 891ef18..fa1889a 100644
31 --- a/drivers/mmc/host/Kconfig
32 +++ b/drivers/mmc/host/Kconfig
33 @@ -55,6 +55,18 @@ config MMC_SDHCI_PCI
34  
35           If unsure, say N.
36  
37 +config MMC_SDHCI_S3C
38 +       tristate "SDHCI support on Samsung S3C SoC"
39 +       depends on MMC_SDHCI && PLAT_S3C24XX
40 +       help
41 +         This selects the Secure Digital Host Controller Interface (SDHCI)
42 +         often referrered to as the HSMMC block in some of the Samsung S3C
43 +         range of SoC.
44 +
45 +         If you have a controller with this interface, say Y or M here.
46 +
47 +         If unsure, say N.
48 +
49  config MMC_RICOH_MMC
50         tristate "Ricoh MMC Controller Disabler  (EXPERIMENTAL)"
51         depends on MMC_SDHCI_PCI
52 diff --git a/arch/arm/plat-s3c/include/plat/sdhci.h b/arch/arm/plat-s3c/include/plat/sdhci.h
53 index f615308..570da2d 100644
54 --- a/arch/arm/plat-s3c/include/plat/sdhci.h
55 +++ b/arch/arm/plat-s3c/include/plat/sdhci.h
56 @@ -29,6 +29,7 @@ struct mmc_ios;
57   *            is necessary the controllers and/or GPIO blocks require the
58   *           changing of driver-strength and other controls dependant on
59   *           the card and speed of operation.
60 + * sdhci_host: Pointer kept during init, allows presence change notification
61   *
62   * Initialisation data specific to either the machine or the platform
63   * for the device driver to use or call-back when configuring gpio or
64 @@ -45,8 +46,11 @@ struct s3c_sdhci_platdata {
65                             void __iomem *regbase,
66                             struct mmc_ios *ios,
67                             struct mmc_card *card);
68 +       struct sdhci_host * sdhci_host;
69  };
70  
71 +extern void sdhci_s3c_force_presence_change(struct platform_device *pdev);
72 +
73  /**
74   * s3c_sdhci0_set_platdata - Set platform data for S3C SDHCI device.
75   * @pd: Platform data to register to device.
76 --- /dev/null
77 +++ b/arch/arm/mach-s3c2410/include/mach/mci.h
78 @@ -0,0 +1,13 @@
79 +#ifndef _ARCH_MCI_H
80 +#define _ARCH_MCI_H
81 +
82 +struct s3c24xx_mci_pdata {
83 +       unsigned int    gpio_detect;
84 +       unsigned int    gpio_wprotect;
85 +       unsigned long   ocr_avail;
86 +       unsigned int    do_dma;
87 +       void            (*set_power)(unsigned char power_mode,
88 +                                    unsigned short vdd);
89 +};
90 +
91 +#endif /* _ARCH_NCI_H */
92 diff --git a/arch/arm/mach-s3c2440/s3c2440.c b/arch/arm/mach-s3c2440/s3c2440.c
93 index ac1f7ea..f7f8f31 100644
94 --- a/arch/arm/mach-s3c2440/s3c2440.c
95 +++ b/arch/arm/mach-s3c2440/s3c2440.c
96 @@ -46,6 +46,9 @@ int __init s3c2440_init(void)
97         s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT;
98         s3c_device_wdt.resource[1].end   = IRQ_S3C2440_WDT;
99  
100 +       /* make sure SD/MMC driver can distinguish 2440 from 2410 */
101 +       s3c_device_sdi.name = "s3c2440-sdi";
102 +
103         /* register our system device for everything else */
104  
105         return sysdev_register(&s3c2440_sysdev);
106 diff --git a/arch/arm/mach-s3c2442/s3c2442.c b/arch/arm/mach-s3c2442/s3c2442.c
107 index 4663bdc..9602d57 100644
108 --- a/arch/arm/mach-s3c2442/s3c2442.c
109 +++ b/arch/arm/mach-s3c2442/s3c2442.c
110 @@ -21,6 +21,7 @@
111  
112  #include <plat/s3c2442.h>
113  #include <plat/cpu.h>
114 +#include <plat/devs.h>
115  
116  static struct sys_device s3c2442_sysdev = {
117         .cls            = &s3c2442_sysclass,
118 @@ -30,5 +31,8 @@ int __init s3c2442_init(void)
119  {
120         printk("S3C2442: Initialising architecture\n");
121  
122 +       /* make sure SD/MMC driver can distinguish 2440 from 2410 */
123 +       s3c_device_sdi.name = "s3c2440-sdi";
124 +
125         return sysdev_register(&s3c2442_sysdev);
126  }
127 diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
128 index cf153f6..c25f464 100644
129 --- a/drivers/mmc/host/Makefile
130 +++ b/drivers/mmc/host/Makefile
131 @@ -13,6 +13,7 @@ endif
132  obj-$(CONFIG_MMC_MXC)          += mxcmmc.o
133  obj-$(CONFIG_MMC_SDHCI)                += sdhci.o
134  obj-$(CONFIG_MMC_SDHCI_PCI)    += sdhci-pci.o
135 +obj-$(CONFIG_MMC_SDHCI_S3C)    += sdhci-s3c.o
136  obj-$(CONFIG_MMC_RICOH_MMC)    += ricoh_mmc.o
137  obj-$(CONFIG_MMC_SDHCI_OF)     += sdhci-of.o
138  obj-$(CONFIG_MMC_SDHCI_PLTFM)  += sdhci-pltfm.o
139 diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
140 index ca1ba3d..7f80047 100644
141 --- a/drivers/mmc/host/s3cmci.h
142 +++ b/drivers/mmc/host/s3cmci.h
143 @@ -8,6 +8,10 @@
144   * published by the Free Software Foundation.
145   */
146  
147 +
148 +#include <mach/regs-sdi.h>
149 +#include <linux/regulator/consumer.h>
150 +
151  /* FIXME: DMA Resource management ?! */
152  #define S3CMCI_DMA 0
153  
154 @@ -68,7 +72,16 @@ struct s3cmci_host {
155         unsigned int            ccnt, dcnt;
156         struct tasklet_struct   pio_tasklet;
157  
158 +       /*
159 +        * Here's where we save the registers during suspend. Note that we skip
160 +        * SDIDATA, which is at different positions on 2410 and 2440, so
161 +        * there's no "+1" in the array size.
162 +        */
163 +       u32                     saved[(S3C2410_SDIIMSK-S3C2410_SDICON)/4];
164 +
165  #ifdef CONFIG_CPU_FREQ
166         struct notifier_block   freq_transition;
167  #endif
168 +
169 +       struct regulator *regulator;
170  };
171 diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
172 index 8c08cd7..b3cded4 100644
173 --- a/drivers/mmc/host/s3cmci.c
174 +++ b/drivers/mmc/host/s3cmci.c
175 @@ -2,6 +2,7 @@
176   *  linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
177   *
178   *  Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
179 + *  Copyright (C) 2007 Harald Welte <laforge@gnumonks.org>
180   *
181   * Current driver maintained by Ben Dooks and Simtec Electronics
182   *  Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
183 @@ -25,9 +26,18 @@
184  
185  #include <mach/regs-sdi.h>
186  #include <mach/regs-gpio.h>
187 +#include <mach/hardware.h>
188  
189  #include <plat/mci.h>
190  
191 +#include <asm/dma.h>
192 +#include <asm/dma-mapping.h>
193 +
194 +#include <asm/io.h>
195 +#include <mach/regs-gpio.h>
196 +#include <mach/mci.h>
197 +#include <mach/dma.h>
198 +
199  #include "s3cmci.h"
200  
201  #define DRIVER_NAME "s3c-mci"
202 @@ -48,6 +58,9 @@ static const int dbgmap_err   = dbg_fail;
203  static const int dbgmap_info  = dbg_info | dbg_conf;
204  static const int dbgmap_debug = dbg_err | dbg_debug;
205  
206 +static int f_max = -1; /* override maximum frequency limit */
207 +static int persist; /* keep interface alive across suspend/resume */
208 +
209  #define dbg(host, channels, args...)             \
210         do {                                      \
211         if (dbgmap_err & channels)                \
212 @@ -281,8 +294,11 @@ static void do_pio_read(struct s3cmci_host *host)
213                  * an even multiple of 4. */
214                 if (fifo >= host->pio_bytes)
215                         fifo = host->pio_bytes;
216 -               else
217 +               else {
218                         fifo -= fifo & 3;
219 +                       if (!fifo)
220 +                               break;
221 +               }
222  
223                 host->pio_bytes -= fifo;
224                 host->pio_count += fifo;
225 @@ -330,7 +346,7 @@ static void do_pio_write(struct s3cmci_host *host)
226  
227         to_ptr = host->base + host->sdidata;
228  
229 -       while ((fifo = fifo_free(host)) > 3) {
230 +       while ((fifo = fifo_free(host))) {
231                 if (!host->pio_bytes) {
232                         res = get_data_buffer(host, &host->pio_bytes,
233                                                         &host->pio_ptr);
234 @@ -354,8 +370,11 @@ static void do_pio_write(struct s3cmci_host *host)
235                  * words, so round down to an even multiple of 4. */
236                 if (fifo >= host->pio_bytes)
237                         fifo = host->pio_bytes;
238 -               else
239 +               else {
240                         fifo -= fifo & 3;
241 +                       if (!fifo)
242 +                               break;
243 +               }
244  
245                 host->pio_bytes -= fifo;
246                 host->pio_count += fifo;
247 @@ -374,7 +393,6 @@ static void pio_tasklet(unsigned long data)
248  {
249         struct s3cmci_host *host = (struct s3cmci_host *) data;
250  
251 -
252         disable_irq(host->irq);
253  
254         if (host->pio_active == XFER_WRITE)
255 @@ -615,7 +633,6 @@ irq_out:
256  
257         spin_unlock_irqrestore(&host->complete_lock, iflags);
258         return IRQ_HANDLED;
259 -
260  }
261  
262  /*
263 @@ -1027,6 +1044,7 @@ static void s3cmci_send_request(struct mmc_host *mmc)
264                         dbg(host, dbg_err, "data prepare error %d\n", res);
265                         cmd->error = res;
266                         cmd->data->error = res;
267 +                       cmd->data->error = -EIO;
268  
269                         mmc_request_done(mmc, mrq);
270                         return;
271 @@ -1264,10 +1282,8 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
272         host->is2440    = is2440;
273  
274         host->pdata = pdev->dev.platform_data;
275 -       if (!host->pdata) {
276 -               pdev->dev.platform_data = &s3cmci_def_pdata;
277 +       if (!host->pdata)
278                 host->pdata = &s3cmci_def_pdata;
279 -       }
280  
281         spin_lock_init(&host->complete_lock);
282         tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
283 @@ -1380,6 +1396,18 @@ static int __devinit s3cmci_probe(struct platform_device *pdev, int is2440)
284         mmc->f_min      = host->clk_rate / (host->clk_div * 256);
285         mmc->f_max      = host->clk_rate / host->clk_div;
286  
287 +       if (f_max >= 0) {
288 +               unsigned f = f_max;
289 +
290 +               if (f < mmc->f_min)
291 +                       f = mmc->f_min;
292 +               if (mmc->f_max > f) {
293 +                       dev_info(&pdev->dev, "f_max lowered from %u to %u Hz\n",
294 +                           mmc->f_max, f);
295 +                       mmc->f_max = f;
296 +               }
297 +       }
298 +
299         if (host->pdata->ocr_avail)
300                 mmc->ocr_avail = host->pdata->ocr_avail;
301  
302 @@ -1492,18 +1520,60 @@ static int __devinit s3cmci_2440_probe(struct platform_device *dev)
303  
304  #ifdef CONFIG_PM
305  
306 +static int save_regs(struct mmc_host *mmc)
307 +{
308 +       struct s3cmci_host *host = mmc_priv(mmc);
309 +       unsigned long flags;
310 +       unsigned from;
311 +       u32 *to = host->saved;
312 +
313 +       mmc_flush_scheduled_work();
314 +
315 +       local_irq_save(flags);
316 +       for (from = S3C2410_SDICON; from != S3C2410_SDIIMSK+4; from += 4)
317 +               if (from != host->sdidata)
318 +                       *to++ = readl(host->base + from);
319 +       BUG_ON(to-host->saved != ARRAY_SIZE(host->saved));
320 +       local_irq_restore(flags);
321 +
322 +       return 0;
323 +}
324 +
325 +static int restore_regs(struct mmc_host *mmc)
326 +{
327 +       struct s3cmci_host *host = mmc_priv(mmc);
328 +       unsigned long flags;
329 +       unsigned to;
330 +       u32 *from = host->saved;
331 +
332 +       /*
333 +        * Before we begin with the necromancy, make sure we don't
334 +        * inadvertently start something we'll regret microseconds later.
335 +        */
336 +       from[S3C2410_SDICMDCON - S3C2410_SDICON] = 0;
337 +
338 +       local_irq_save(flags);
339 +       for (to = S3C2410_SDICON; to != S3C2410_SDIIMSK+4; to += 4)
340 +               if (to != host->sdidata)
341 +                       writel(*from++, host->base + to);
342 +       BUG_ON(from-host->saved != ARRAY_SIZE(host->saved));
343 +       local_irq_restore(flags);
344 +
345 +       return 0;
346 +}
347 +
348  static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
349  {
350         struct mmc_host *mmc = platform_get_drvdata(dev);
351  
352 -       return  mmc_suspend_host(mmc, state);
353 +       return persist ? save_regs(mmc) : mmc_suspend_host(mmc, state);
354  }
355  
356  static int s3cmci_resume(struct platform_device *dev)
357  {
358         struct mmc_host *mmc = platform_get_drvdata(dev);
359  
360 -       return mmc_resume_host(mmc);
361 +       return persist ? restore_regs(mmc) : mmc_resume_host(mmc);
362  }
363  
364  #else /* CONFIG_PM */
365 @@ -1561,9 +1631,13 @@ static void __exit s3cmci_exit(void)
366  module_init(s3cmci_init);
367  module_exit(s3cmci_exit);
368  
369 +module_param(f_max, int, 0644);
370 +module_param(persist, int, 0644);
371 +
372  MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
373  MODULE_LICENSE("GPL v2");
374  MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>, Ben Dooks <ben-linux@fluff.org>");
375  MODULE_ALIAS("platform:s3c2410-sdi");
376  MODULE_ALIAS("platform:s3c2412-sdi");
377  MODULE_ALIAS("platform:s3c2440-sdi");
378 +
379