* published by the Free Software Foundation.
*/
-#include <asm/arch/mmc.h>
-#include <asm/arch/menelaus.h>
-#include <asm/arch/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/menelaus.h>
-#ifdef CONFIG_MMC_OMAP
+#include <asm/mach-types.h>
+
+#include <mach/mmc.h>
+#include <mach/gpio.h>
+
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
static const int slot_switch_gpio = 96;
-static const int slot1_wp_gpio = 23;
-static const int slot2_wp_gpio = 8;
-static int slot1_cover_closed;
-static int slot2_cover_closed;
+
+static const int n810_slot2_pw_vddf = 23;
+static const int n810_slot2_pw_vdd = 9;
+
+static int slot1_cover_open;
+static int slot2_cover_open;
static struct device *mmc_device;
/*
- * VMMC --> slot 1
- * VDCDC3_APE, VMCS2_APE --> slot 2
+ * VMMC --> slot 1 (N800 & N810)
+ * VDCDC3_APE, VMCS2_APE --> slot 2 on N800
* GPIO96 --> Menelaus GPIO2
+ * GPIO23 --> controls slot2 VSD (N810 only)
+ * GPIO9 --> controls slot2 VIO_SD (N810 only)
*/
static int n800_mmc_switch_slot(struct device *dev, int slot)
return 0;
}
-static int n800_mmc_set_power(struct device *dev, int slot, int power_on,
- int vdd)
+static int n800_mmc_set_power_menelaus(struct device *dev, int slot,
+ int power_on, int vdd)
{
int mV;
case MMC_VDD_28_29:
mV = 2800;
break;
- case MMC_VDD_18_19:
+ case MMC_VDD_165_195:
mV = 1850;
break;
default:
mV = 2200;
break;
case MMC_VDD_20_21:
- case MMC_VDD_19_20:
mV = 2000;
break;
- case MMC_VDD_18_19:
- case MMC_VDD_17_18:
+ case MMC_VDD_165_195:
mV = 1800;
break;
- case MMC_VDD_150_155:
- case MMC_VDD_145_150:
- mV = 1500;
- break;
default:
BUG();
}
return 0;
}
+static void nokia_mmc_set_power_internal(struct device *dev,
+ int power_on)
+{
+ dev_dbg(dev, "Set internal slot power %s\n",
+ power_on ? "on" : "off");
+
+ if (power_on) {
+ omap_set_gpio_dataout(n810_slot2_pw_vddf, 1);
+ udelay(30);
+ omap_set_gpio_dataout(n810_slot2_pw_vdd, 1);
+ udelay(100);
+ } else {
+ omap_set_gpio_dataout(n810_slot2_pw_vdd, 0);
+ msleep(50);
+ omap_set_gpio_dataout(n810_slot2_pw_vddf, 0);
+ msleep(50);
+ }
+}
+
+static int n800_mmc_set_power(struct device *dev, int slot, int power_on,
+ int vdd)
+{
+ if (machine_is_nokia_n800() || slot == 0)
+ return n800_mmc_set_power_menelaus(dev, slot, power_on, vdd);
+
+ nokia_mmc_set_power_internal(dev, power_on);
+
+ return 0;
+}
+
static int n800_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
{
int r;
-#ifdef CONFIG_MMC_DEBUG
dev_dbg(dev, "Set slot %d bus mode %s\n", slot + 1,
bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
-#endif
BUG_ON(slot != 0 && slot != 1);
slot++;
switch (bus_mode) {
return r;
}
-#if 0
-static int n800_mmc_get_ro(struct device *dev, int slot)
-{
- int ro;
-
- slot++;
- if (slot == 1)
- ro = omap_get_gpio_datain(slot1_wp_gpio);
- else
- ro = omap_get_gpio_datain(slot2_wp_gpio);
-#ifdef CONFIG_MMC_DEBUG
- dev_dbg(dev, "Get RO slot %d: %s\n",
- slot, ro ? "read-only" : "read-write");
-#endif
- return ro;
-}
-#endif
-
static int n800_mmc_get_cover_state(struct device *dev, int slot)
{
slot++;
BUG_ON(slot != 1 && slot != 2);
if (slot == 1)
- return slot1_cover_closed;
+ return slot1_cover_open;
else
- return slot2_cover_closed;
+ return slot2_cover_open;
}
static void n800_mmc_callback(void *data, u8 card_mask)
{
- if (card_mask & (1 << 1))
- slot2_cover_closed = 0;
+ int bit, *openp, index;
+
+ if (machine_is_nokia_n800()) {
+ bit = 1 << 1;
+ openp = &slot2_cover_open;
+ index = 1;
+ } else {
+ bit = 1;
+ openp = &slot1_cover_open;
+ index = 0;
+ }
+
+ if (card_mask & bit)
+ *openp = 1;
else
- slot2_cover_closed = 1;
- omap_mmc_notify_cover_event(mmc_device, 1, slot2_cover_closed);
+ *openp = 0;
+
+ omap_mmc_notify_cover_event(mmc_device, index, *openp);
}
-void n800_mmc_slot1_cover_handler(void *arg, int state)
+void n800_mmc_slot1_cover_handler(void *arg, int closed_state)
{
if (mmc_device == NULL)
return;
- slot1_cover_closed = state;
- omap_mmc_notify_cover_event(mmc_device, 0, state);
+ slot1_cover_open = !closed_state;
+ omap_mmc_notify_cover_event(mmc_device, 0, closed_state);
}
static int n800_mmc_late_init(struct device *dev)
{
- int r;
+ int r, bit, *openp;
+ int vs2sel;
mmc_device = dev;
if (r < 0)
return r;
+ if (machine_is_nokia_n800())
+ vs2sel = 0;
+ else
+ vs2sel = 2;
+
+ r = menelaus_set_mmc_slot(2, 0, vs2sel, 1);
+ if (r < 0)
+ return r;
+
+ n800_mmc_set_power(dev, 0, MMC_POWER_ON, 16); /* MMC_VDD_28_29 */
+ n800_mmc_set_power(dev, 1, MMC_POWER_ON, 16);
+
r = menelaus_set_mmc_slot(1, 1, 0, 1);
if (r < 0)
return r;
- r = menelaus_set_mmc_slot(2, 1, 0, 1);
+ r = menelaus_set_mmc_slot(2, 1, vs2sel, 1);
if (r < 0)
return r;
if (r < 0)
return r;
- if (r & (1 << 1))
- slot2_cover_closed = 1;
+ if (machine_is_nokia_n800()) {
+ bit = 1 << 1;
+ openp = &slot2_cover_open;
+ } else {
+ bit = 1;
+ openp = &slot1_cover_open;
+ slot2_cover_open = 0;
+ }
+
+ /* All slot pin bits seem to be inversed until first swith change */
+ if (r == 0xf || r == (0xf & ~bit))
+ r = ~r;
+
+ if (r & bit)
+ *openp = 1;
else
- slot2_cover_closed = 0;
+ *openp = 0;
r = menelaus_register_mmc_callback(n800_mmc_callback, NULL);
return r;
}
+static void n800_mmc_shutdown(struct device *dev)
+{
+ int vs2sel;
+
+ if (machine_is_nokia_n800())
+ vs2sel = 0;
+ else
+ vs2sel = 2;
+
+ menelaus_set_mmc_slot(1, 0, 0, 0);
+ menelaus_set_mmc_slot(2, 0, vs2sel, 0);
+}
+
static void n800_mmc_cleanup(struct device *dev)
{
menelaus_unregister_mmc_callback();
+
+ omap_free_gpio(slot_switch_gpio);
+
+ if (machine_is_nokia_n810()) {
+ omap_free_gpio(n810_slot2_pw_vddf);
+ omap_free_gpio(n810_slot2_pw_vdd);
+ }
}
-static struct omap_mmc_platform_data n800_mmc_data = {
- .enabled = 1,
+/*
+ * MMC controller1 has two slots that are multiplexed via I2C.
+ * MMC controller2 is not in use.
+ */
+static struct omap_mmc_platform_data mmc1_data = {
.nr_slots = 2,
- .wire4 = 1,
.switch_slot = n800_mmc_switch_slot,
.init = n800_mmc_late_init,
.cleanup = n800_mmc_cleanup,
+ .shutdown = n800_mmc_shutdown,
+ .max_freq = 24000000,
+ .dma_mask = 0xffffffff,
.slots[0] = {
+ .wire4 = 1,
.set_power = n800_mmc_set_power,
.set_bus_mode = n800_mmc_set_bus_mode,
- .get_ro = NULL,
.get_cover_state= n800_mmc_get_cover_state,
- .ocr_mask = MMC_VDD_18_19 | MMC_VDD_28_29 | MMC_VDD_30_31 |
- MMC_VDD_32_33 | MMC_VDD_33_34,
+ .ocr_mask = MMC_VDD_165_195 | MMC_VDD_30_31 |
+ MMC_VDD_32_33 | MMC_VDD_33_34,
.name = "internal",
},
.slots[1] = {
.set_power = n800_mmc_set_power,
.set_bus_mode = n800_mmc_set_bus_mode,
- .get_ro = NULL,
.get_cover_state= n800_mmc_get_cover_state,
- .ocr_mask = MMC_VDD_150_155 | MMC_VDD_145_150 | MMC_VDD_17_18 |
- MMC_VDD_18_19 | MMC_VDD_19_20 | MMC_VDD_20_21 |
+ .ocr_mask = MMC_VDD_165_195 | MMC_VDD_20_21 |
MMC_VDD_21_22 | MMC_VDD_22_23 | MMC_VDD_23_24 |
MMC_VDD_24_25 | MMC_VDD_27_28 | MMC_VDD_28_29 |
MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_32_33 |
},
};
+static struct omap_mmc_platform_data *mmc_data[OMAP24XX_NR_MMC];
+
void __init n800_mmc_init(void)
+
{
- omap_set_mmc_info(1, &n800_mmc_data);
+ if (machine_is_nokia_n810()) {
+ n800_mmc1_data.slots[0].name = "external";
+
+ /*
+ * Some Samsung Movinand chips do not like open-ended
+ * multi-block reads and fall to braind-dead state
+ * while doing so. Reducing the number of blocks in
+ * the transfer or delays in clock disable do not help
+ */
+ n800_mmc1_data.slots[1].name = "internal";
+ n800_mmc1_data.slots[1].ban_openended = 1;
+ }
+
if (omap_request_gpio(slot_switch_gpio) < 0)
BUG();
omap_set_gpio_dataout(slot_switch_gpio, 0);
omap_set_gpio_direction(slot_switch_gpio, 0);
- if (omap_request_gpio(slot1_wp_gpio) < 0)
- BUG();
- if (omap_request_gpio(slot2_wp_gpio) < 0)
- BUG();
- omap_set_gpio_direction(slot1_wp_gpio, 1);
- omap_set_gpio_direction(slot2_wp_gpio, 1);
-}
+ if (machine_is_nokia_n810()) {
+ if (omap_request_gpio(n810_slot2_pw_vddf) < 0)
+ BUG();
+ omap_set_gpio_dataout(n810_slot2_pw_vddf, 0);
+ omap_set_gpio_direction(n810_slot2_pw_vddf, 0);
+
+ if (omap_request_gpio(n810_slot2_pw_vdd) < 0)
+ BUG();
+ omap_set_gpio_dataout(n810_slot2_pw_vdd, 0);
+ omap_set_gpio_direction(n810_slot2_pw_vdd, 0);
+ }
+
+ mmc_data[0] = &mmc1_data;
+ omap2_init_mmc(mmc_data, OMAP24XX_NR_MMC);
+}
#else
void __init n800_mmc_init(void)