summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPradeep Gurumath <pradeep@pradeep-poky.(none)>2009-12-02 14:23:02 -0500
committerSantosh Shilimkar <santosh.shilimkar@ti.com>2009-12-04 15:26:38 +0530
commitb2cdd4a704988c54377df324d92ab977cfa734ea (patch)
tree295d63a3e9fd1c46bc714b2dd3965532cac82176
parentd298ae1f582675e414ddd4e22b98eeee9abbf7b8 (diff)
WLAN: Adapting the opensource SDIO drivers for use by WiFi driver Signed-off-by: Pradeep Gurumath <pradeepgurumath@ti.com>ti-2.6.31-omap4-L24.1-p1
Signed-off-by: Pradeep Gurumath <pradeep@pradeep-poky.(none)>
-rw-r--r--arch/arm/include/asm/mach/mmc.h13
-rw-r--r--arch/arm/mach-omap2/Kconfig22
-rw-r--r--arch/arm/mach-omap2/Makefile1
-rwxr-xr-xarch/arm/mach-omap2/board-4430sdp-wifi.c138
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-omap2/board-4430sdp.c55
-rw-r--r--arch/arm/mach-omap2/mmc-twl4030.c55
-rw-r--r--arch/arm/plat-omap/include/mach/mmc.h11
-rwxr-xr-xarch/arm/plat-omap/include/mach/wifi_tiwlan.h23
-rw-r--r--drivers/mmc/core/Kconfig7
-rw-r--r--drivers/mmc/core/core.c17
-rw-r--r--drivers/mmc/core/sdio.c95
-rw-r--r--drivers/mmc/core/sdio_bus.c11
-rw-r--r--drivers/mmc/core/sdio_io.c7
-rw-r--r--drivers/mmc/host/omap_hsmmc.c50
-rw-r--r--include/linux/mmc/card.h9
-rw-r--r--include/linux/mmc/host.h23
-rw-r--r--include/linux/mmc/sdio_func.h10
-rw-r--r--include/linux/mmc/sdio_ids.h5
18 files changed, 545 insertions, 7 deletions
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
index 4da332b03144..36c860347b66 100644
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -6,6 +6,19 @@
#include <linux/mmc/host.h>
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+
+struct embedded_sdio_data {
+ struct sdio_cis cis;
+ struct sdio_cccr cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+ unsigned int quirks;
+};
+#endif
+
struct mmc_platform_data {
unsigned int ocr_mask; /* available voltages */
u32 (*translate_vdd)(struct device *, unsigned int);
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index a755eb5e2361..3f8f74734987 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -60,6 +60,28 @@ config MACH_OMAP3EVM
bool "OMAP 3530 EVM board"
depends on ARCH_OMAP3 && ARCH_OMAP34XX
+config WIFI_CONTROL_FUNC
+ bool "Enable WiFi control function abstraction"
+ depends on MACH_OMAP_4430SDP
+ default Y
+ help
+ Enables Power/Reset/Carddetect function abstraction
+config TIWLAN_SDIO
+ bool "TI WLAN Enhanced SDIO Contoller support"
+ depends on MMC_OMAP || MMC_OMAP_MODULE || MMC_OMAP_HS || MMC_OMAP_HS_MODULE
+ help
+ Say Y here if you want to be able to use TI's WLAN device using the
+ SDIO interface. If unsure, say N.
+config TIWLAN_MMC_CONTROLLER
+ int "MMC Controller number that TI WLAN chip is connected to"
+ range 1 5
+ depends on TIWLAN_SDIO || MMC_EMBEDDED_SDIO
+ default "5"
+ help
+ Choose the number of the MMC controller that TI WLAN chip is
+ connected to. TI WLAN has SDIO host controller that will control
+ this MMC port.
+
config MACH_OMAP3_PANDORA
bool "OMAP3 Pandora"
depends on ARCH_OMAP3 && ARCH_OMAP34XX
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index a6cea7e84825..7e7dc29786bb 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_MACH_OMAP_ZOOM2) += board-zoom2.o \
obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \
mmc-twl4030.o
+obj-$(CONFIG_MMC_EMBEDDED_SDIO) += board-4430sdp-wifi.o
# Platform specific device init code
obj-y += usb-musb.o \
usb-ehci.o
diff --git a/arch/arm/mach-omap2/board-4430sdp-wifi.c b/arch/arm/mach-omap2/board-4430sdp-wifi.c
new file mode 100755
index 000000000000..4789cbc32ea0
--- /dev/null
+++ b/arch/arm/mach-omap2/board-4430sdp-wifi.c
@@ -0,0 +1,138 @@
+/*
+ * Board support file for containing WiFi specific details for OMAP4430 SDP.
+ *
+ * Copyright (C) 2009 Texas Instruments
+ *
+ * Author: Pradeep Gurumath <pradeepgurumath@ti.com>
+ *
+ * Based on mach-omap2/board-3430sdp.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/* linux/arch/arm/mach-omap2/board-4430sdp-wifi.c
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/err.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <mach/wifi_tiwlan.h>
+
+#define SDP4430_WIFI_PMENA_GPIO 54
+#define SDP4430_WIFI_IRQ_GPIO 53
+
+static int sdp4430_wifi_cd; /* WIFI virtual 'card detect' status */
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+
+int omap_wifi_status_register(void (*callback)(int card_present,
+ void *dev_id), void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+
+ wifi_status_cb_devid = dev_id;
+
+ return 0;
+}
+
+int omap_wifi_status(int irq)
+{
+ return sdp4430_wifi_cd;
+}
+
+int sdp4430_wifi_set_carddetect(int val)
+{
+ printk(KERN_WARNING"%s: %d\n", __func__, val);
+ sdp4430_wifi_cd = val;
+ if (wifi_status_cb)
+ wifi_status_cb(val, wifi_status_cb_devid);
+ else
+ printk(KERN_WARNING "%s: Nobody to notify\n", __func__);
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(sdp4430_wifi_set_carddetect);
+#endif
+
+static int sdp4430_wifi_power_state;
+
+int sdp4430_wifi_power(int on)
+{
+ printk(KERN_WARNING"%s: %d\n", __func__, on);
+ gpio_set_value(SDP4430_WIFI_PMENA_GPIO, on);
+ sdp4430_wifi_power_state = on;
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(sdp4430_wifi_power);
+#endif
+
+static int sdp4430_wifi_reset_state;
+int sdp4430_wifi_reset(int on)
+{
+ printk(KERN_WARNING"%s: %d\n", __func__, on);
+ sdp4430_wifi_reset_state = on;
+ return 0;
+}
+#ifndef CONFIG_WIFI_CONTROL_FUNC
+EXPORT_SYMBOL(sdp4430_wifi_reset);
+#endif
+
+struct wifi_platform_data sdp4430_wifi_control = {
+ .set_power = sdp4430_wifi_power,
+ .set_reset = sdp4430_wifi_reset,
+ .set_carddetect = sdp4430_wifi_set_carddetect,
+};
+
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+static struct resource sdp4430_wifi_resources[] = {
+ [0] = {
+ .name = "device_wifi_irq",
+ .start = OMAP_GPIO_IRQ(SDP4430_WIFI_IRQ_GPIO),
+ .end = OMAP_GPIO_IRQ(SDP4430_WIFI_IRQ_GPIO),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
+ },
+};
+
+static struct platform_device sdp4430_wifi_device = {
+ .name = "device_wifi",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(sdp4430_wifi_resources),
+ .resource = sdp4430_wifi_resources,
+ .dev = {
+ .platform_data = &sdp4430_wifi_control,
+ },
+};
+#endif
+
+static int __init sdp4430_wifi_init(void)
+{
+ int ret;
+
+ printk(KERN_WARNING"%s: start\n", __func__);
+ ret = gpio_request(SDP4430_WIFI_IRQ_GPIO, "wifi_irq");
+ if (ret < 0) {
+ printk(KERN_ERR "%s: can't reserve GPIO: %d\n", __func__,
+ SDP4430_WIFI_IRQ_GPIO);
+ goto out;
+ }
+ gpio_direction_input(SDP4430_WIFI_IRQ_GPIO);
+#ifdef CONFIG_WIFI_CONTROL_FUNC
+ ret = platform_device_register(&sdp4430_wifi_device);
+#endif
+out:
+ return ret;
+}
+
+device_initcall(sdp4430_wifi_init);
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 1fb4976e6e18..a1e9e32eb5c9 100644..100755
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -338,26 +338,26 @@ static struct twl4030_hsmmc_info mmc[] = {
{
.mmc = 2,
.wires = 8,
- .gpio_cd = -EINVAL,
+ .gpio_cd = -EINVAL,
.gpio_wp = -EINVAL,
},
{
.mmc = 3,
.wires = -EINVAL,
- .gpio_cd = -EINVAL,
+ .gpio_cd = -EINVAL,
.gpio_wp = -EINVAL,
},
{
.mmc = 4,
.wires = -EINVAL,
- .gpio_cd = -EINVAL,
+ .gpio_cd = -EINVAL,
.gpio_wp = -EINVAL,
},
{
.mmc = 5,
- .wires = -EINVAL,
+ .wires = 8,
.gpio_cd = -EINVAL,
- .gpio_wp = -EINVAL,
+ .gpio_wp = 4,
},
{} /* Terminator */
};
@@ -384,6 +384,14 @@ static int __init sdp4430_mmc_init(void)
{
/* Hard Coding Values for testing */
mmc[0].gpio_cd = 373;
+
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ /* The controller that is connected to the 128x device
+ should have the card detect gpio disabled. This is
+ achieved by initializing it with a negative value */
+ mmc[CONFIG_TIWLAN_MMC_CONTROLLER - 1].gpio_cd = -EINVAL;
+#endif
+
twl4030_mmc_init(mmc);
/* link regulators to MMC adapters ... we "know" the
* regulators will be set up only *after* we return.
@@ -725,6 +733,38 @@ static struct omap_usbhost_port_data sdp_usbhost_port_data[] = {
},
};
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+static void pad_config(unsigned long pad_addr, u32 andmask, u32 ormask)
+{
+ int val;
+ u32 *addr;
+
+ addr = (u32 *) ioremap(pad_addr, 4);
+ if (!addr) {
+ printk(KERN_ERR"OMAP_pad_config: ioremap failed with addr %lx\n",
+ pad_addr);
+ return;
+ }
+
+ val = __raw_readl(addr);
+ val &= andmask;
+ val |= ormask;
+ __raw_writel(val, addr);
+
+ iounmap(addr);
+}
+
+void wlan_1283_config()
+{
+ pad_config(0x4A100078, 0xFFECFFFF, 0x00030000);
+ pad_config(0x4A10007C, 0xFFFFFFEF, 0x0000000B);
+ if (gpio_request(54, NULL) != 0)
+ printk(KERN_ERR "GPIO 54 request failed\n");
+ gpio_direction_output(54, 0);
+ return ;
+}
+#endif
+
static void __init omap_4430sdp_init(void)
{
omap4_i2c_init();
@@ -740,6 +780,11 @@ static void __init omap_4430sdp_init(void)
ARRAY_SIZE(sdp4430_spi_board_info));
omap_mcbsp_init();
sdp4430_mmc_init();
+
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ wlan_1283_config();
+#endif
+
sdp4430_display_init();
omap_phoenix_init();
#ifdef CONFIG_NOP_USB_XCEIV
diff --git a/arch/arm/mach-omap2/mmc-twl4030.c b/arch/arm/mach-omap2/mmc-twl4030.c
index 68891f26f951..aa84fed1edc6 100644
--- a/arch/arm/mach-omap2/mmc-twl4030.c
+++ b/arch/arm/mach-omap2/mmc-twl4030.c
@@ -24,6 +24,11 @@
#include <mach/mmc.h>
#include <mach/board.h>
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <asm/mach/mmc.h>
+#include <linux/mmc/sdio_ids.h>
+#endif
+
#include "mmc-twl4030.h"
#include <linux/i2c/twl.h>
@@ -460,6 +465,43 @@ static int twl_mmc23_set_power(struct device *dev, int slot, int power_on, int v
static struct omap_mmc_platform_data *hsmmc_data[OMAP44XX_NR_MMC] __initdata;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+int omap_wifi_status_register(void (*callback)(int card_present,
+ void *dev_id), void *dev_id);
+int omap_wifi_status(int irq);
+
+static struct sdio_embedded_func wifi_func_array[] = {
+ {
+ .f_class = SDIO_CLASS_BT_A,
+ .f_maxblksize = 512,
+ },
+ {
+ .f_class = SDIO_CLASS_WLAN,
+ .f_maxblksize = 512,
+ },
+};
+
+static struct embedded_sdio_data omap_wifi_emb_data = {
+ .cis = {
+ .vendor = SDIO_VENDOR_ID_TI,
+ .device = SDIO_DEVICE_ID_TI_WL12xx,
+ .blksize = 512,
+ .max_dtr = 24000000,
+ },
+ .cccr = {
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 1,
+ .high_power = 0,
+ .high_speed = 0,
+ .disable_cd = 1,
+ },
+ .funcs = wifi_func_array,
+ .num_funcs = 2,
+ .quirks = MMC_QUIRK_VDD_165_195 | MMC_QUIRK_LENIENT_FUNC0,
+};
+#endif
+
void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
{
struct twl4030_hsmmc_info *c;
@@ -513,6 +555,16 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
else
snprintf(twl->name, ARRAY_SIZE(twl->name),
"mmc%islot%i", c->mmc, 1);
+
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (c->mmc == CONFIG_TIWLAN_MMC_CONTROLLER) {
+ mmc->slots[0].embedded_sdio = &omap_wifi_emb_data;
+ mmc->slots[0].register_status_notify =
+ &omap_wifi_status_register;
+ mmc->slots[0].card_detect = &omap_wifi_status;
+ }
+#endif
+
mmc->slots[0].name = twl->name;
mmc->nr_slots = 1;
mmc->slots[0].wires = c->wires;
@@ -598,6 +650,9 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
case 5:
/* TODO */
mmc->slots[0].set_power = twl_mmc23_set_power;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ mmc->slots[0].ocr_mask = MMC_VDD_165_195;
+#endif
break;
default:
pr_err("MMC%d configuration not supported!\n", c->mmc);
diff --git a/arch/arm/plat-omap/include/mach/mmc.h b/arch/arm/plat-omap/include/mach/mmc.h
index c84a6d43bf90..5836aadd87c9 100644
--- a/arch/arm/plat-omap/include/mach/mmc.h
+++ b/arch/arm/plat-omap/include/mach/mmc.h
@@ -15,6 +15,10 @@
#include <linux/device.h>
#include <linux/mmc/host.h>
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <asm/mach/mmc.h>
+#endif
+
#include <mach/board.h>
#define OMAP15XX_NR_MMC 1
@@ -111,6 +115,13 @@ struct omap_mmc_platform_data {
unsigned int ban_openended:1;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ struct embedded_sdio_data *embedded_sdio;
+ int (*register_status_notify)
+ (void (*callback)(int card_present, void *dev_id),
+ void *dev_id);
+#endif
+
} slots[OMAP_MMC_MAX_SLOTS];
};
diff --git a/arch/arm/plat-omap/include/mach/wifi_tiwlan.h b/arch/arm/plat-omap/include/mach/wifi_tiwlan.h
new file mode 100755
index 000000000000..b0332b04ddc9
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/wifi_tiwlan.h
@@ -0,0 +1,23 @@
+/* mach/wifi_tiwlan.h
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _LINUX_WIFI_TIWLAN_H_
+#define _LINUX_WIFI_TIWLAN_H_
+
+struct wifi_platform_data {
+ int (*set_power)(int val);
+ int (*set_reset)(int val);
+ int (*set_carddetect)(int val);
+ void *(*mem_prealloc)(int section, unsigned long size);
+};
+
+#endif
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index ab37a6d9d32a..81fe13735947 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -14,3 +14,10 @@ config MMC_UNSAFE_RESUME
This option is usually just for embedded systems which use
a MMC/SD card for rootfs. Most people should say N here.
+config MMC_EMBEDDED_SDIO
+ boolean "MMC embedded SDIO device support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ If you say Y here, support will be added for embedded SDIO
+ devices which do not contain the necessary enumeration
+ support in hardware to be properly detected.
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index d84c880fac84..47a90cb62939 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1028,6 +1028,23 @@ EXPORT_SYMBOL(mmc_resume_host);
#endif
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs,
+ unsigned int quirks)
+{
+ host->embedded_sdio_data.cis = cis;
+ host->embedded_sdio_data.cccr = cccr;
+ host->embedded_sdio_data.funcs = funcs;
+ host->embedded_sdio_data.num_funcs = num_funcs;
+ host->embedded_sdio_data.quirks = quirks;
+}
+EXPORT_SYMBOL(mmc_set_embedded_sdio_data);
+#endif
+
static int __init mmc_init(void)
{
int ret;
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index fb99ccff9080..4784073f3af3 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -24,6 +24,10 @@
#include "sdio_ops.h"
#include "sdio_cis.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/sdio_ids.h>
+#endif
+
static int sdio_read_fbr(struct sdio_func *func)
{
int ret;
@@ -164,6 +168,33 @@ static int sdio_enable_wide(struct mmc_card *card)
return 0;
}
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+/*
+ * Disconnect the pull-up resistor on CD/DAT[3] if requested by the card.
+ * If card detection is not needed, this can save some power.
+ */
+static int sdio_disable_cd(struct mmc_card *card)
+{
+ int ret;
+ u8 ctrl;
+
+ if (!card->cccr.disable_cd)
+ return 0;
+
+ ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
+ if (ret)
+ return ret;
+
+ ctrl |= SDIO_BUS_CD_DISABLE;
+
+ ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+#endif
+
/*
* Test if the card supports high-speed mode and, if so, switch to it.
*/
@@ -275,12 +306,18 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
ocr &= ~0x7F;
}
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (!host->embedded_sdio_data.quirks & MMC_QUIRK_VDD_165_195) {
+#endif
if (ocr & MMC_VDD_165_195) {
printk(KERN_WARNING "%s: SDIO card claims to support the "
"incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
}
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
host->ocr = mmc_select_voltage(host, ocr);
@@ -314,6 +351,11 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
*/
funcs = (ocr & 0x70000000) >> 28;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs)
+ funcs = host->embedded_sdio_data.num_funcs;
+#endif
+
/*
* Allocate card structure.
*/
@@ -323,6 +365,10 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
goto err;
}
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ card->quirks = host->embedded_sdio_data.quirks;
+#endif
+
card->type = MMC_TYPE_SDIO;
card->sdio_funcs = funcs;
@@ -351,9 +397,25 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
/*
* Read the common registers.
*/
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cccr)
+ memcpy(&card->cccr, host->embedded_sdio_data.cccr,
+ sizeof(struct sdio_cccr));
+ else {
+#endif
err = sdio_read_cccr(card);
if (err)
goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
+
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.cis)
+ memcpy(&card->cis, host->embedded_sdio_data.cis,
+ sizeof(struct sdio_cis));
+ else {
+#endif
/*
* Read the common CIS tuples.
@@ -361,6 +423,9 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
err = sdio_read_common_cis(card);
if (err)
goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
/*
* Switch to high-speed (if supported).
@@ -391,13 +456,43 @@ int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
if (err)
goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ /*
+ * Disconnect card detection pull-up resistor (if requested by card).
+ */
+ err = sdio_disable_cd(card);
+ if (err)
+ goto remove;
+#endif
+
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0;i < funcs;i++) {
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (host->embedded_sdio_data.funcs) {
+ struct sdio_func *tmp;
+
+ tmp = sdio_alloc_func(host->card);
+ if (IS_ERR(tmp))
+ goto remove;
+ tmp->num = (i + 1);
+ card->sdio_func[i] = tmp;
+ tmp->class = host->embedded_sdio_data.funcs[i].f_class;
+ tmp->max_blksize =
+ host->embedded_sdio_data.funcs[i].f_maxblksize;
+ tmp->vendor = card->cis.vendor;
+ tmp->device = card->cis.device;
+ } else {
+#endif
+
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ }
+#endif
+
}
mmc_release_host(host);
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index 46284b527397..05dd55f35110 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -20,6 +20,10 @@
#include "sdio_cis.h"
#include "sdio_bus.h"
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#include <linux/mmc/host.h>
+#endif
+
#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
@@ -202,6 +206,13 @@ static void sdio_release_func(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ /*
+ * If this device is embedded then we never allocated
+ * cis tables for this func
+ */
+ if (!func->card->host->embedded_sdio_data.funcs)
+#endif
sdio_free_func_cis(func);
if (func->info)
diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c
index f61fc2d4cd0a..de4ad9d4321a 100644
--- a/drivers/mmc/core/sdio_io.c
+++ b/drivers/mmc/core/sdio_io.c
@@ -624,7 +624,14 @@ void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
BUG_ON(!func);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+/*allow SDIO FN0 writes outside of VS CCCR*/
+#define MMC_QUIRK_LENIENT_FUNC0 (1<<1)
+ if ((addr < 0xF0 || addr > 0xFF) &&
+ (!func->card->quirks & MMC_QUIRK_LENIENT_FUNC0)) {
+#else
if (addr < 0xF0 || addr > 0xFF) {
+#endif
if (err_ret)
*err_ret = -EINVAL;
return;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 96f595954604..7577c501502d 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -918,10 +918,14 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->power_mode) {
case MMC_POWER_OFF:
- mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
+ if (mmc_slot(host).set_power)
+ mmc_slot(host).set_power(host->dev,
+ host->slot_id, 0, 0);
break;
case MMC_POWER_UP:
- mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd);
+ if (mmc_slot(host).set_power)
+ mmc_slot(host).set_power(host->dev,
+ host->slot_id, 1, ios->vdd);
break;
}
@@ -996,6 +1000,27 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
OMAP_HSMMC_READ(host->base, CON) | OD);
}
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+static void omap_hsmmc_status_notify_cb(int card_present, void *dev_id)
+{
+ struct mmc_omap_host *host = dev_id;
+ struct omap_mmc_slot_data *slot = &mmc_slot(host);
+
+ printk(KERN_DEBUG "%s: card_present %d\n", mmc_hostname(host->mmc),
+ card_present);
+
+ host->carddetect = slot->card_detect(slot->card_detect_irq);
+
+ sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
+ if (host->carddetect) {
+ mmc_detect_change(host->mmc, (HZ * 200) / 1000);
+ } else {
+ mmc_omap_reset_controller_fsm(host, SRD);
+ mmc_detect_change(host->mmc, (HZ * 50) / 1000);
+ }
+}
+#endif
+
static int omap_hsmmc_get_cd(struct mmc_host *mmc)
{
struct mmc_omap_host *host = mmc_priv(mmc);
@@ -1142,6 +1167,19 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
}
#endif
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ if (pdev->id == CONFIG_TIWLAN_MMC_CONTROLLER-1) {
+ if (pdata->slots[0].embedded_sdio != NULL) {
+ mmc_set_embedded_sdio_data(mmc,
+ &pdata->slots[0].embedded_sdio->cis,
+ &pdata->slots[0].embedded_sdio->cccr,
+ pdata->slots[0].embedded_sdio->funcs,
+ pdata->slots[0].embedded_sdio->num_funcs,
+ pdata->slots[0].embedded_sdio->quirks);
+ }
+ }
+#endif
+
platform_set_drvdata(pdev, host);
INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect);
@@ -1272,6 +1310,14 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
goto err_irq_cd;
}
}
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ else if (mmc_slot(host).register_status_notify) {
+ if (pdev->id == CONFIG_TIWLAN_MMC_CONTROLLER-1) {
+ mmc_slot(host).register_status_notify(
+ omap_hsmmc_status_notify_cb, host);
+ }
+ }
+#endif
OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK);
OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK);
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 403aa505f27e..8e691e5f619a 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -62,7 +62,12 @@ struct sdio_cccr {
low_speed:1,
wide_bus:1,
high_power:1,
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ high_speed:1,
+ disable_cd:1;
+#else
high_speed:1;
+#endif
};
struct sdio_cis {
@@ -95,6 +100,10 @@ struct mmc_card {
#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ u32 quirks; /* card quirks */
+#endif
+
u32 raw_cid[4]; /* raw card CID */
u32 raw_csd[4]; /* raw card CSD */
u32 raw_scr[2]; /* raw card SCR */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 3e7615e9087e..024582ca344b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -161,6 +161,20 @@ struct mmc_host {
struct dentry *debugfs_root;
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+ struct {
+ struct sdio_cis *cis;
+ struct sdio_cccr *cccr;
+ struct sdio_embedded_func *funcs;
+ int num_funcs;
+ unsigned int quirks; /* embedded sdio card quirks */
+/* do not ignore MMC_VDD_165_195 */
+#define MMC_QUIRK_VDD_165_195 (1<<0)
+/* allow SDIO FN0 writes outside of VS CCCR*/
+#define MMC_QUIRK_LENIENT_FUNC0 (1<<1)
+ } embedded_sdio_data;
+#endif
+
unsigned long private[0] ____cacheline_aligned;
};
@@ -169,6 +183,15 @@ extern int mmc_add_host(struct mmc_host *);
extern void mmc_remove_host(struct mmc_host *);
extern void mmc_free_host(struct mmc_host *);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+extern void mmc_set_embedded_sdio_data(struct mmc_host *host,
+ struct sdio_cis *cis,
+ struct sdio_cccr *cccr,
+ struct sdio_embedded_func *funcs,
+ int num_funcs,
+ unsigned int quirks);
+#endif
+
static inline void *mmc_priv(struct mmc_host *host)
{
return (void *)host->private;
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index 451bdfc85830..8a0dbf273268 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -20,6 +20,16 @@ struct sdio_func;
typedef void (sdio_irq_handler_t)(struct sdio_func *);
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+/*
+ * Structure used to hold embedded SDIO device data from platform layer
+ */
+struct sdio_embedded_func {
+ uint8_t f_class;
+ uint32_t f_maxblksize;
+};
+#endif
+
/*
* SDIO function CIS tuple (unknown to the core)
*/
diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
index 39751c8cde9c..e3da8ef517a1 100644
--- a/include/linux/mmc/sdio_ids.h
+++ b/include/linux/mmc/sdio_ids.h
@@ -28,6 +28,11 @@
#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
#define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105
+#ifdef CONFIG_MMC_EMBEDDED_SDIO
+#define SDIO_VENDOR_ID_TI 0x104c
+#define SDIO_DEVICE_ID_TI_WL12xx 0x9066
+#endif
+
#define SDIO_VENDOR_ID_SIANO 0x039a
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
#define SDIO_DEVICE_ID_SIANO_NICE 0x0202