hh.org updates
[hh.org.git] / arch / arm / mach-pxa / h3900 / h3900_mmc.c
blobbcaeb67ebe21eefb55d32917a7df56f7664f5209
1 /*
2 * MMC glue driver for iPAQ h3900
4 * Copyright (c) 2005 Phil Blundell
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/kernel.h>
14 #include <linux/tty.h>
15 #include <linux/sched.h>
16 #include <linux/delay.h>
17 #include <linux/pm.h>
18 #include <linux/err.h>
19 #include <linux/soc-old.h>
20 #include <linux/soc/tmio_mmc.h>
22 #include <asm/mach-types.h>
23 #include <asm/hardware.h>
24 #include <asm/setup.h>
26 #include <asm/mach/arch.h>
27 #include <asm/arch/h3900-asic.h>
29 #include <linux/soc/asic2_base.h>
30 #include <linux/soc/asic3_base.h>
32 extern struct platform_device h3900_asic2, h3900_asic3;
34 static unsigned long shared;
35 static int clock_enabled;
37 #define asic2 &h3900_asic2.dev
38 #define asic3 &h3900_asic3.dev
40 static void h3900_set_mmc_clock (struct platform_device *sdev, int status)
42 switch (status) {
43 case MMC_CLOCK_ENABLED:
44 if (!clock_enabled) {
45 unsigned long val, flags;
47 /* Enable clock from asic2 */
48 asic2_shared_add (asic2, &shared, ASIC_SHARED_CLOCK_EX1);
49 asic2_clock_enable (asic2, ASIC2_CLOCK_SD_2, 1);
50 asic3_set_clock_cdex (asic3, CLOCK_CDEX_EX0, CLOCK_CDEX_EX0);
51 mdelay (1);
53 /* Enable the host clock */
54 asic3_set_clock_sel (asic3, CLOCK_SEL_SD_HCLK_SEL | CLOCK_SEL_SD_BCLK_SEL, CLOCK_SEL_SD_HCLK_SEL);
55 asic3_set_clock_cdex (asic3, CLOCK_CDEX_SD_HOST | CLOCK_CDEX_SD_BUS, CLOCK_CDEX_SD_HOST);
56 mdelay(1);
57 asic3_set_clock_cdex (asic3, CLOCK_CDEX_EX1, CLOCK_CDEX_EX1);
59 local_irq_save (flags);
60 val = asic3_read_register (asic3, _IPAQ_ASIC3_EXTCF_Base + _IPAQ_ASIC3_EXTCF_Select);
61 val |= ASIC3_EXTCF_SD_MEM_ENABLE;
62 asic3_write_register (asic3, _IPAQ_ASIC3_EXTCF_Base + _IPAQ_ASIC3_EXTCF_Select, val);
64 val = asic3_read_register (asic3, _IPAQ_ASIC3_SDHWCTRL_Base + _IPAQ_ASIC3_SDHWCTRL_SDConf);
65 val &= ~(ASIC3_SDHWCTRL_SUSPEND | ASIC3_SDHWCTRL_PCLR); // wake up
66 asic3_write_register (asic3, _IPAQ_ASIC3_SDHWCTRL_Base + _IPAQ_ASIC3_SDHWCTRL_SDConf, val);
67 local_irq_restore (flags);
69 mdelay(10);
71 clock_enabled = 1;
72 printk("h3900_mmc: clock enabled\n");
73 } else {
74 printk(KERN_ERR "h3900_mmc: clock was already enabled\n");
76 break;
78 default:
79 if (clock_enabled) {
80 asic2_clock_enable (asic2, ASIC2_CLOCK_SD_2, 0);
81 asic2_shared_release (asic2, &shared, ASIC_SHARED_CLOCK_EX1);
82 clock_enabled = 0;
83 printk("h3900_mmc: clock disabled\n");
84 } else {
85 printk(KERN_ERR "h3900_mmc: clock was already disabled\n");
87 break;
91 static struct tmio_mmc_hwconfig h3900_mmc_hwconfig = {
92 .set_mmc_clock = h3900_set_mmc_clock,
93 .address_shift = 1,
96 static int
97 h3900_mmc_probe (struct device *dev)
99 struct platform_device *pdev = to_platform_device (dev);
100 int mmc_irq;
102 if (pdev->num_resources == 0)
103 return -ENODEV;
105 mmc_irq = (int)pdev->resource[0].start;
107 return asic3_register_mmc (asic3, mmc_irq, &h3900_mmc_hwconfig);
110 static int
111 h3900_mmc_remove (struct device *dev)
113 return asic3_unregister_mmc (asic3);
116 static struct device_driver h3900_mmc_device_driver = {
117 .name = "h3900 mmc",
118 .bus = &platform_bus_type,
120 .probe = h3900_mmc_probe,
121 .remove = h3900_mmc_remove,
124 static int __init
125 h3900_mmc_init (void)
127 int retval = 0;
128 retval = driver_register (&h3900_mmc_device_driver);
129 return retval;
132 static void __exit
133 h3900_mmc_exit (void)
135 driver_unregister (&h3900_mmc_device_driver);
138 module_init (h3900_mmc_init)
139 module_exit (h3900_mmc_exit)
141 MODULE_LICENSE("GPL");
142 MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
143 MODULE_DESCRIPTION("Glue driver for h3900 MMC");
144 MODULE_SUPPORTED_DEVICE("h3900_mmc");