diff options
author | Pradeep Gurumath <pradeep@pradeep-poky.(none)> | 2009-12-02 14:23:02 -0500 |
---|---|---|
committer | Santosh Shilimkar <santosh.shilimkar@ti.com> | 2009-12-04 15:26:38 +0530 |
commit | b2cdd4a704988c54377df324d92ab977cfa734ea (patch) | |
tree | 295d63a3e9fd1c46bc714b2dd3965532cac82176 | |
parent | d298ae1f582675e414ddd4e22b98eeee9abbf7b8 (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.h | 13 | ||||
-rw-r--r-- | arch/arm/mach-omap2/Kconfig | 22 | ||||
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 1 | ||||
-rwxr-xr-x | arch/arm/mach-omap2/board-4430sdp-wifi.c | 138 | ||||
-rwxr-xr-x[-rw-r--r--] | arch/arm/mach-omap2/board-4430sdp.c | 55 | ||||
-rw-r--r-- | arch/arm/mach-omap2/mmc-twl4030.c | 55 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/mach/mmc.h | 11 | ||||
-rwxr-xr-x | arch/arm/plat-omap/include/mach/wifi_tiwlan.h | 23 | ||||
-rw-r--r-- | drivers/mmc/core/Kconfig | 7 | ||||
-rw-r--r-- | drivers/mmc/core/core.c | 17 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 95 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_bus.c | 11 | ||||
-rw-r--r-- | drivers/mmc/core/sdio_io.c | 7 | ||||
-rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 50 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 9 | ||||
-rw-r--r-- | include/linux/mmc/host.h | 23 | ||||
-rw-r--r-- | include/linux/mmc/sdio_func.h | 10 | ||||
-rw-r--r-- | include/linux/mmc/sdio_ids.h | 5 |
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 |