1/05/2012

Porting to different the MMC interface


Author : Dicky Chiang
Date : Dec 2012

Introduce:
This document describes how to porting to a different the MMC interface.
All presentation will be based on the DM37x platform with Linux kernel 2.6.37 and NLCP WLAN driver.On Dm37x WLAN SDIO is connected to MMC2 bus. For example, the following will porting to MMC3 interface.

Step 1:
In file kernel/arch/arm/mach-omap2/board-omap3evm.c
Modify Initialization and pin-muxing if using any other MMC bus.
static struct omap2_hsmmc_info mmc[] = {
{
.mmc = 1,
.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
.gpio_wp = 29,
},
#ifdef CONFIG_WL12XX_PLATFORM_DATA
{
.name = "wl1271",
.mmc = 3,
.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD,
.gpio_wp = -EINVAL,
.gpio_cd = -EINVAL,
.nonremovable = true,
},
#endif
{} /* Terminator */
};
static struct omap_board_mux board_mux[] __initdata = {
#ifdef CONFIG_WL12XX_PLATFORM_DATA
/* WLAN IRQ - GPIO 112 */
OMAP3_MUX(CSI2_DX0, OMAP_MUX_MODE4 | OMAP_PIN_INPUT),
/* WLAN POWER ENABLE - GPIO 16 */
OMAP3_MUX(ETK_D2, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT),
/* MMC3 SDIO pin muxes for WL12xx */
OMAP3_MUX(ETK_CLK, OMAP_MUX_MODE2 |
OMAP_PIN_INPUT_PULLUP),//CLK
OMAP3_MUX(ETK_CTL, OMAP_MUX_MODE2 |
OMAP_PIN_INPUT_PULLUP),//CMD
OMAP3_MUX(ETK_D3, OMAP_MUX_MODE2 |
OMAP_PIN_INPUT_PULLUP),//DATA3
OMAP3_MUX(ETK_D6, OMAP_MUX_MODE2 |
OMAP_PIN_INPUT_PULLUP),//DATA2
OMAP3_MUX(ETK_D5, OMAP_MUX_MODE2 |
OMAP_PIN_INPUT_PULLUP),//DATA1
OMAP3_MUX(ETK_D4, OMAP_MUX_MODE2 |
OMAP_PIN_INPUT_PULLUP),//DATA0
//USB gpio40
OMAP3_MUX(GPMC_A7, OMAP_MUX_MODE4 | OMAP_PIN_OUTPUT ),
#endif
{ .reg_offset = OMAP_MUX_TERMINATOR },
};

Modify regulator supply to register in kernel when the probe host MMC slot.
static struct regulator_consumer_supply panther_vmmc2_supply =
REGULATOR_SUPPLY("vmmc", "mmci-omap-hs.2")


Step 2.
In file kernel/arch/arm/mach-omap2/devices.c
Add to supported MMC3 host controller at omap2_mmc_mux func.
/* For MMC3 the pins need to be muxed in the board-*.c files */
if (controller_nr == 2) {
/* MMC3 */
omap_mux_init_signal("sdmmc3_clk",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc3_cmd",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc3_dat0",
OMAP_PIN_INPUT_PULLUP);
/*
* For 8 wire configurations, Lines DAT4, 5, 6 and 7 need to be muxed
* in the board-*.c files
*/
if (mmc_controller->slots[0].caps &
(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) {
omap_mux_init_signal("sdmmc3_dat1",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc3_dat2",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc3_dat3",
OMAP_PIN_INPUT_PULLUP);
}
if (mmc_controller->slots[0].caps &
MMC_CAP_8_BIT_DATA) {
omap_mux_init_signal("sdmmc3_dat4.sdmmc3_dat4",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc3_dat5.sdmmc3_dat5",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc3_dat6.sdmmc3_dat6",
OMAP_PIN_INPUT_PULLUP);
omap_mux_init_signal("sdmmc3_dat7.sdmmc3_dat7",
OMAP_PIN_INPUT_PULLUP);
}
}


Step 3.
In file kernla/arch/arm/mach-omap2/mux34xx.c
Add the pin mux for support MMC3 interface. ( According the schematics and TRM)
static struct omap_mux __initdata omap3_muxmodes[] = {
……..
//MMC3_CLK
_OMAP3_MUXENTRY(ETK_CLK, 12,
"etk_clk", "mcbsp5_clkx", "sdmmc3_clk", "hsusb1_stp",
"gpio_12", "mm1_rxdp", "hsusb1_tll_stp", "hw_dbg0"),
//MMC3_CMD
_OMAP3_MUXENTRY(ETK_CTL, 13,
"etk_ctl", NULL, "sdmmc3_cmd", "hsusb1_clk",
"gpio_13", NULL, "hsusb1_tll_clk", "hw_dbg1"),
//MMC3_DATA0
_OMAP3_MUXENTRY(ETK_D4, 18,
"etk_d4", "mcbsp5_dr", "sdmmc3_data0", "hsusb1_data4",
"gpio_18", NULL, "hsusb1_tll_data4", "hw_dbg6"),
//MMC3_DATA1
_OMAP3_MUXENTRY(ETK_D5, 19,
"etk_d5", "mcbsp5_fsx", "sdmmc3_data1", "hsusb1_data5",
"gpio_19", NULL, "hsusb1_tll_data5", "hw_dbg7"),
//MMC3_DATA2
_OMAP3_MUXENTRY(ETK_D6, 20,
"etk_d6", "mcbsp5_dx", "sdmmc3_data2", "hsusb1_data6",
"gpio_20", NULL, "hsusb1_tll_data6", "hw_dbg8"),
//MMC3_DATA3
_OMAP3_MUXENTRY(ETK_D6, 17,
"etk_d3", "mcbspi3_clk", "sdmmc3_data3", "hsusb1_data7",
"gpio_17", NULL, "hsusb1_tll_data7", "hw_dbg5")
………..
}

Step 4.
In file u-boot/ti/evm/evm.h
Add the mux configuration for that pin at u-boot.
#define MUX_EVM() \
………
/* SDIO Interface to WIFI Module */ \
MUX_VAL(CP(ETK_CLK_ES2), (IEN | PTU | EN | M4)) /*MMC3_CLK*/\
MUX_VAL(CP(ETK_CTL_ES2), (IEN | PTU | EN | M4)) /*MMC3_CMD*/\
MUX_VAL(CP(ETK_D4_ES2), (IEN | PTU | EN | M4))/*MMC3_DATA0*/\
MUX_VAL(CP(ETK_D5_ES2), (IEN | PTU | EN | M4))/*MMC3_DATA1*/\
MUX_VAL(CP(ETK_D6_ES2), (IEN | PTU | EN | M4))/*MMC3_DATA2*/\
MUX_VAL(CP(ETK_D3_ES2), (IEN | PTU | EN | M4))/*MMC3_DATA3*/\
……..
#endif

Porting wg7210(WL1251) with Linux 2.6.37 on AM18


Below is port of wg7210 info for kernel 2.6.37 with Linux on the AM18 platform.

First, your can get patches and nlcp driver ( modifies to wg7210 ) with firmware 4.0.4.32 from my git.

kernel patches ( include firmware for wg7210 ):

$ git clone git://github.com/dickychiang/am18-wg7210-patch.git

compat-wireless-r4-11-wg7210 :

$ git clone git://github.com/dickychiang/compat-wireless-r4-11.git

The driver will support iw tool to configure wireless.

Also the porting of wg7210 need to take care to important the following :

a) wg7210 has inside the eeprom. To fill structure in wl12xx_platform_data of "use_eeprom".

In board-*.c file :


static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = {                                                              

    .irq = -1,
    .board_ref_clock = CONFIG_DA850_MISTRAL_WL12XX_REFCLOCK,
    .platform_quirks = WL12XX_PLATFORM_QUIRK_EDGE_IRQ,
    .use_eeprom = 1,
};

If already, in insert wl1251.ko module will be R/W to eeprom and create a network interface. ( Note, check the irq is work well and connected to EVB.)

And then insert wl1251_sdio.ko will be download firmware into chip.

wl1251: chip id 0x7020101 (1251 PG11)
wl1251: firmware booted (Rev 4.0.4.3.5)

b)  Since it the SDIO of CCCR,CIS,FBR is in not corrent. so kernel can not to get right information to match for wg7210. My ideas is give the card information to kernel such as vendor and device id which let kernel to know the sdio device is match to my sdio driver for wg7210.

In drivers/mmc/core/sdio.c :

b-1) Mark the read cccr,cis at mmc_sdio_init_card func.
                                                  
#ifndef CONFIG_WG7210                                                                                  
    err = sdio_read_cccr(card);                                                                        
    if (err)                                                                                                                          
        goto remove;                                                                                   
#endif                                                                                                 
                                                                                
#ifndef CONFIG_WG7210                                                                                  
    err = sdio_read_common_cis(card);                                                                  
    if (err)                                                                                           
        goto remove;                                                                                   
#endif

To fill the card''s maximum speed with clock. ( Default the value is get from cccr register )

#ifdef CONFIG_WG7210
    card->cis.max_dtr = 25000000;
#endif
    mmc_set_clock(host, mmc_sdio_get_max_clock(card));
b-2) Mark the read fbr and set the id's info to structure sdio_func at sdio_init_func.

#ifndef CONFIG_WG7210                                                                                  
    if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) {                                                     
        ret = sdio_read_fbr(func);                                                                     
        if (ret)                                                                                       
            goto fail;                                                                                 
                                                                                                       
        ret = sdio_read_func_cis(func);                                                                
        if (ret)
            goto fail;                                                                                 
    } else {                                                                                           
        func->vendor = func->card->cis.vendor;                                                         
        func->device = func->card->cis.device;                                                         
        func->max_blksize = func->card->cis.blksize;                                                   
    }                                                                                                  
#else                                                                                                  
    func->vendor = 0x104c;                                                                             
    func->device = 0x9066;                                                                             
    func->max_blksize = 4096;                                                                          
#endif  

Note, the max block size value is depend on mmc host controller, if not to match wii be failed in send CMD52.


c) Modify the download firmware path in nlcp driver :

In compat-wireless/drivers/net/wireless/wl1251/wl1251.h

#define WL1251_FW_NAME "ti-connectivity/wl1251-fw.bin"
#define WL1251_NVS_NAME "ti-connectivity/wl1251-nvs.bin"


d) If want to debug NLCP driver,do it to following. ( The method can used to wl12xx )

    look at compat-wireless/driver/net/wireless/wl1251/wl1251.h, you can find its.

enum {
    DEBUG_NONE  = 0,
    DEBUG_IRQ   = BIT(0),
    DEBUG_SPI   = BIT(1),
    DEBUG_BOOT  = BIT(2),
    DEBUG_MAILBOX   = BIT(3),
    DEBUG_NETLINK   = BIT(4),
    DEBUG_EVENT = BIT(5),
    DEBUG_TX    = BIT(6),
    DEBUG_RX    = BIT(7),
    DEBUG_SCAN  = BIT(8),
    DEBUG_CRYPT = BIT(9),
    DEBUG_PSM   = BIT(10),
    DEBUG_MAC80211  = BIT(11),
    DEBUG_CMD   = BIT(12),
    DEBUG_ACX   = BIT(13),
    DEBUG_ALL   = ~0,
};

#define DEBUG_LEVEL (DEBUG_NONE)

To modifies the DEBUG_LEVEL for you want to check the driver info. Suppose I wonder to see the BOOT and MAC80211 information :
 DEBUG_BOOT  = BIT(2)             =>    (1 << 2)  =  0x4
 DEBUG_MAC80211  = BIT(11)     =>   (1 << 11) =  0x800

so the answer :

#deinfe DEBUG_LEVEL   0x804

Re-build it now.   And then to entry the file system ( For example with Linux based ) and enable all kernel prints level.
echo 8 > /proc/sys/kernel/printk

Running daemon for print the kernel logs :

cat /proc/kmsg &

Your can see the debug message when insert wl1251.ko ( or wl12xx.ko ) and wl1251_sdio.ko ( or wl12xx_sdio.ko ).