Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

WL127x Porting Guide

From Texas Instruments Wiki
Jump to: navigation, search

Host connection to the WiLink connectivity module


AM335x COM8-HW.png

The WiLink connectivity module (wl12xx/wl18xx) is connected to the host processor using two main hardware interface and a couple of GPIO pins:

  • The WLAN subsystem is connected using an MMC interface running is SDIO mode and two GPIO pins.
    • One GPIO is used for controlling power to the WLAN subsystem (WLAN Enable)
    • The second GPIO pin is used for incoming interrupts from the WLAN subsystem (WLAN IRQ).


  • The Bluetooth, GPS are all sharing a UART connection (using CTS/RTS flow control) and is using an additional GPIO for power controll (BT Enable).


GPIO design considerations

  • For the WLAN Enable pin and BT/GPS enable pins we should select pins that can have Pull-UP enabled on them for alllowing these pins to remain "high" during suspend.
  • For the WLAN interrupt pin it is preffered that we select a GPIO ping that is capable of waking up the host from suspend. This is needed in case we would like to activate the "Wake on WLAN feature" where the system could be waked up based on a configures specific packet recived from the air.


WLAN Interface

The following table describes the WLAN Host Interface (SDIO, WLAN enable, WLAN IRQ)

The Host interface the WLAN with SDIO interface. The WLAN chip acts as a slave with the HOST (OMAP and so on) as the master. that is the host generates the SDIO clock and read/write from the WLAN interface.

WLAN-HOST Interface Signals - SDIO

Signal Name Description
SDIO_CMD SDIO Command line. This is a bidirectional line. The host sends commands and the WLAN responds to these commands.
SDIO_CLK SDIO Clock line. This line is generated by the host.
SDIO_D0 SDIO Data 0 line. This is the primary data line used in both 1-bit and 4-bit SDIO mode. This is a bidirectional line.
SDIO_D1 SDIO Data 1 line. This is one of four data lines. This line is used only in 4-bit mode. This is a bidirectional line.
SDIO_D2 SDIO Data 2 line. This is one of four data lines. This line is used only in 4-bit mode. This is a bidirectional line.
SDIO_D3 SDIO Data 3 line. This is one of four data lines. This line is used only in 4- bit mode. This is a bidirectional line.
WLAN_Enable WLAN enable signal, should be "1" in order enable the WLAN operation, once the WLAN enable signal is "0" the WLAN part of the chip is reseted in a way that the firmware has to be loaded again after enabling the WLAN.
WLAN IRQ generate Interrupt from the WLAN chip toward the HOST, it is used to signal the HOST on many events like received data from the WLAN media is ready at the firmware (WLAN Chip) quque, or that the last Tx frame was transmitted, all king of asynchronous messages (evens) and so on.


Bluetooth Interface

Bluetooth uses the HCI interface to connect to the host. The WL18xx supports both H4 (4-wire) interfaces. UART type.

BT-HOST Interface Signals - UART

Signal Name Description
UART RX UART - Receive data.
UART TX UART - transmit data.
RTS Request to Send. (flow control)
CTS Clear to Send . (flow control)
BT Enable Enable the BT operation, has to be set to "1" in order to enable the BT part.


Following is the way HOST is connected to the WL18xx chip through the UART (Bluetooth HCI Interface - H4 Connectivity)
Solution Hardware-Figure4.jpg

Flow Control:
RTS / CTS is used to Flow Control. RTS (Request to Send) and CTS (Clear to Send). These two lines allow the receiver and the transmitter to alert each other to their state. A transmitter raises its RTS line indicating to the reciver that it request to send data. And If the receiver is in a position to receive the data it will assert its CTS line, acknpwlaging the transmit side that it can start transmitting. Using the RTS/CTS enables device drivers which implement hardware flow control code to maintain a reliable data connection between transmitter and receiver.

Antennas

WLAN Antenna:
in order to get good RF performance for WLAN and BT, one needs to connect an external Antenna. Following are the ordering links for recomended WLAN/BT Antenna and RF cable:
Antenna: Please refer to the following link <WLAN RF Antenna>
RF Cable: Please refer to the following link <RF cable>


Board Initialization file


  • For each platform there is a board file inside the PSP where the initial board related software bringup is done
  • The board file is located inside the linux kernel source tree under “arch/arm/mach-(omap2/davinci)/board-xxxx.c


NOTE:This presentation is mostly based on board_am335x-evm.c located under “arch/arm/mach-omap2” however similar initilization is needed for every new platform which is connecting to the same type of WiLink module.

Initialization sequence

  • Inside the am335x-evm board file there is an initialization function called am335x_evm_setup(). This function is called when the Linux kernel boots.
  • For the standard am335x-evm this function is calling setup_general_purpose_evm() function which sets up all the peripherals that are in use.
  • This function initiates calls to all the initialization functions which are located inside the gen_purp_evm_dev_cfg[] device configuration structure.
  • The functions which are related to the connectivity parts are mmc2_wl12xx_init, uart1_wl12xx_init and wl12xx_init. These functions are descibed below.


MMC initialization

  • The WLAN subsystem is usually connected using a second MMC port of the host processor. The first MMC is normally used for SD card.
  • On the Aam335x-evm MMC3 is being used and an init function is provided for initializing this MMC structure data.
  • Before we can use the SDIO pins they need to be muxed in the kernel for SDIO mode.

The SDIO related pin muxes are set in the structure below:

/* Module pin mux for wlan and bluetooth */
static struct pinmux_config mmc2_wl12xx_pin_mux[] = {
	{"gpmc_a1.mmc2_dat0", OMAP_MUX_MODE3 | AM33XX_PIN_INPUT_PULLUP},
	{"gpmc_a2.mmc2_dat1", OMAP_MUX_MODE3 | AM33XX_PIN_INPUT_PULLUP},
	{"gpmc_a3.mmc2_dat2", OMAP_MUX_MODE3 | AM33XX_PIN_INPUT_PULLUP},
	{"gpmc_ben1.mmc2_dat3", OMAP_MUX_MODE3 | AM33XX_PIN_INPUT_PULLUP},
	{"gpmc_csn3.mmc2_cmd", OMAP_MUX_MODE3 | AM33XX_PIN_INPUT_PULLUP},
	{"gpmc_clk.mmc2_clk", OMAP_MUX_MODE3 | AM33XX_PIN_INPUT_PULLUP},
	{NULL, 0},
};

The below function is used for setting up this MMC interface. In this function the pin mux structure is being intialized and then the inteface is initilized as a non-removeable card working in 4-Bit mode.

static void mmc2_wl12xx_init(int evm_id, int profile)
{
	setup_pin_mux(mmc2_wl12xx_pin_mux);

	am335x_mmc[1].mmc = 3;
	am335x_mmc[1].name = "wl1271";
	am335x_mmc[1].caps = MMC_CAP_4_BIT_DATA | MMC_CAP_POWER_OFF_CARD;
	am335x_mmc[1].nonremovable = true;
	am335x_mmc[1].gpio_cd = -EINVAL;
	am335x_mmc[1].gpio_wp = -EINVAL;
	am335x_mmc[1].ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34; /* 3V3 */

	/* mmc will be initialized when mmc0_init is called */
	return;
}
  • The mmc2_wl12xx_init() function is placed inside the board configuration structure seen below and when it is called during board startup a second mmc interface is created in the kernel (mmc1) which is used for communicating with the WLAN sub-system.
/* General Purpose EVM */
static struct evm_dev_cfg gen_purp_evm_dev_cfg[] = {
	{enable_ecap0,	DEV_ON_DGHTR_BRD, (PROFILE_0 | PROFILE_1 |
						PROFILE_2 | PROFILE_7) },
	{lcdc_init,	DEV_ON_DGHTR_BRD, (PROFILE_0 | PROFILE_1 |
						PROFILE_2 | PROFILE_7) },
...
...
	{mmc1_init,	DEV_ON_DGHTR_BRD, PROFILE_2},
	{mmc2_wl12xx_init,	DEV_ON_BASEBOARD, (PROFILE_0 | PROFILE_3 |
								PROFILE_5)},
	{mmc0_init,	DEV_ON_BASEBOARD, (PROFILE_ALL & ~PROFILE_5)},
	{mmc0_no_cd_init,	DEV_ON_BASEBOARD, PROFILE_5},
	{spi0_init,	DEV_ON_DGHTR_BRD, PROFILE_2},
	{uart1_wl12xx_init,	DEV_ON_BASEBOARD, (PROFILE_0 | PROFILE_3 |
								PROFILE_5)},
	{wl12xx_init,	DEV_ON_BASEBOARD, (PROFILE_0 | PROFILE_3 | PROFILE_5)},
...
...
	{haptics_init,	DEV_ON_DGHTR_BRD, (PROFILE_4)},
	{NULL, 0, 0},
};

UART Initialization

  • The UART is used for communicating with the rest of the connectivity sub-systems (Bluetooth, GPS, .)
  • On the am335x-evm UART1 is used and we need to mux it's pins into UART mode in order to work with it.

The structure below is used for performing this pin muxing. In strucure we mux the four UART pins (Rx,Tx,CTS,RTS)

static struct pinmux_config uart1_wl12xx_pin_mux[] = {
	{"uart1_ctsn.uart1_ctsn", OMAP_MUX_MODE0 | AM33XX_PIN_OUTPUT},
	{"uart1_rtsn.uart1_rtsn", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT},
	{"uart1_rxd.uart1_rxd", OMAP_MUX_MODE0 | AM33XX_PIN_INPUT_PULLUP},
	{"uart1_txd.uart1_txd", OMAP_MUX_MODE0 | AM33XX_PULL_ENBL},
	{NULL, 0},
};

The uart1_wl12xx_init() functions use this structure for performing the UART pin muxing:

static void uart1_wl12xx_init(int evm_id, int profile)
{
	setup_pin_mux(uart1_wl12xx_pin_mux);
}

GPIO Pins Configuration


  • In addition to the MMC, UART intefcaes, the WiLink module needs three additional GPIO lines:
    • WLAN Enable (GPIO1_16): Set as an output pin
    • WLAN Irq (GPIO3_17): Set as a GPIO input pin
    • BT/GPS Enable (GPIO3_21): Set as a GPIO output pin with pull-up enabled. The pullup is needed for keeping the chip enabled when system is suspending so we would not need to releoad the firmware after each suspend/resume cycle.


The strucure below is used for setting the muxing of these pins:

static struct pinmux_config wl12xx_pin_mux[] = {
	{"gpmc_a0.gpio1_16", OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT},
	{"mcasp0_ahclkr.gpio3_17", OMAP_MUX_MODE7 | AM33XX_PIN_INPUT},
	{"mcasp0_ahclkx.gpio3_21", OMAP_MUX_MODE7 | AM33XX_PIN_OUTPUT_PULLUP},
	{NULL, 0},
 };

Power Control


The power control mechanism is used for turning the WLAN sub system on/off based on system commands such as:

  • down/up of the interfcae (ifconfig wlan0 down/up...)
  • Suspend/resume


This is done by registering a GPIO with the MMC device that is enabled (high) when the inteface is brought up and is disabled (low) when the interface is brought down.

There are two methods which can be used for implementing this GPIO control:

  • Using a fixed voltage regulator
  • Using a set_power() function registered with the MMMC device


Power Control – using a fixed voltage regulator


  • For power up/down we can initialize a software(GPIO based) fixed voltage regulator structure that is used for controlling the WLAN_Enable pin by the kernel power manager.
  • For the AM37x platform, this is defined by the structure called “omap3evm_wlan_regulator” and its supporting structures (omap3evm_vwlan, omap3evm_vmmc2, omap3evm_vmmc2_supply).

In the structure below we are setting a fixed voltage reulator which is using a GPIO(43).
The voltage level on this pin(1.8V) is hardware controlled and is only set here for indecation to the user.
The driver is setting a 70msec start-up delay used to allow the module power to stabilize after startup.

#define GPIO_WIFI_PMENA		43
...
...
static struct regulator_consumer_supply omap3evm_vmmc2_supply =
       REGULATOR_SUPPLY("vmmc", "mmci-omap-hs.1");

/* VMMC2 for driving the WL12xx module */
static struct regulator_init_data omap3evm_vmmc2 = {
       .constraints = {
               .valid_ops_mask = REGULATOR_CHANGE_STATUS,
       },
       .num_consumer_supplies  = 1,
       .consumer_supplies = &omap3evm_vmmc2_supply,
};

static struct fixed_voltage_config omap3evm_vwlan = {
       .supply_name            = "vwl1271",
       .microvolts             = 1800000, /* 1.80V */
       .gpio                   = OMAP3EVM_WLAN_PMENA_GPIO,
       .startup_delay          = 70000, /* 70ms */
       .enable_high            = 1,
       .enabled_at_boot        = 0,
       .init_data              = &omap3evm_vmmc2,
};

static struct platform_device omap3evm_wlan_regulator = {
       .name           = "reg-fixed-voltage",
       .id             = 1,
       .dev = {
               .platform_data  = &omap3evm_vwlan,
       },
};

This regulator device is then registered with the platform using the platform_device_register() function as seen below:

static void __init omap3_evm_wl12xx_init(void)
{
#ifdef CONFIG_WL12XX_PLATFORM_DATA
	int ret;

	/* WL12xx WLAN Init */
	omap3evm_wlan_data.irq = gpio_to_irq(OMAP3EVM_WLAN_IRQ_GPIO);
	ret = wl12xx_set_platform_data(&omap3evm_wlan_data);
	if (ret)
		pr_err("error setting wl12xx data: %d\n", ret);
	ret = platform_device_register(&omap3evm_wlan_regulator);
	if (ret)
		pr_err("error registering wl12xx device: %d\n", ret);
#endif
}


Power Control – without voltage regulator


  • An altenrative to using a fixed voltage regulator is using a specific function for power on/off operations.
  • This function can be registered with the MMC driver for power control of the wl12xx module.
  • See the below example of such an off on function used with the am335x-evm board.


static int wl12xx_set_power(struct device *dev, int slot, int on, int vdd)
{
	if (on) {
		gpio_set_value(am335xevm_wlan_data.wlan_enable_gpio, 1);
		mdelay(70);
	}
	else
		gpio_set_value(am335xevm_wlan_data.wlan_enable_gpio, 0);

	return 0;
}


Power Control – registering the set_power function


  • Once created, the wl12xx_set_power function can be registered with the MMC device connected to the wl12xx module.
  • This is done by initializing the “.set_power” element of the respective “omap_mmc_platform_data” structure
  • In the example below MMC1 is used for WLAN
  • Once initialized each time the wlan0 interface is brought up/down this function would be called by the mmc driver.


static void wl12xx_init(int evm_id, int profile)
{
	struct device *dev;
	struct omap_mmc_platform_data *pdata;

…
…
	dev = am335x_mmc[1].dev;
	if (!dev) {
		pr_err("wl12xx mmc device initialization failed\n");
		goto out;
	}

	pdata = dev->platform_data;
	if (!pdata) {
		pr_err("Platfrom data of wl12xx device not set\n");
		goto out;
	}

…
…
	pdata->slots[0].set_power = wl12xx_set_power;
out:
	return;
}


Platform Data


  • The platfrom_data structure is used for passing initialization data to the wl12xx driver.
  • This structure is initializaed inside the board file when the kernel boots up and later read and used by the wl12xx module when it is being loaded.
  • This structure holds information about:
    • Interrupt line used for WLAN
    • Reference clocks (in nlcp the ref clock is NOT read from the ini file)
    • Platform specific quircks
  • Below is the definition of the wl12xx_platform_data structure


struct wl12xx_platform_data {
	/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
	int irq;
	bool use_eeprom;
	int board_ref_clock;
	int board_tcxo_clock;
	unsigned long platform_quirks;
};


  • In the example below for AM335x platform we are setting the structure to use a 38.4Mhz crystal clock and the WLAN_IRQ pin is set to GPIO 113 (GPIO3_17).
  • For the am335x platform we are setting an edge interrupt using the platform_quirks field. This is not needed in case of working with a platform that uses level interrupts.
  • In case of using a quatro device (wl128x) instead of wl127x/wl18xx device we also need to setup the board_tcxo_clock structure element.


#define AM335XEVM_WLAN_IRQ_GPIO		GPIO_TO_PIN(3, 17)

struct wl12xx_platform_data am335xevm_wlan_data = {
	.irq = OMAP_GPIO_IRQ(AM335XEVM_WLAN_IRQ_GPIO),
	.board_ref_clock = WL12XX_REFCLOCK_38_XTAL, /* 38.4Mhz */
	.platform_quirks = WL12XX_PLATFORM_QUIRK_EDGE_IRQ;
};


Reference clock values


  • The available REF_CLOCK values and TCXO_CLOCK values are defined in the include/linux/wl12xx.h
  • Each module vendor may use a different clock setup and the values set in the platform_data structure should match the hardware design of each respective module used.
  • Unfortunately in case of using a different module for the same platform (such as when switching between different types of COMx modules) we need to re-compile the kernel (uImage) after setting the right value based on the module vendor data.
  • Even though the module ini file holds an entry for setting the ref_clock, it is not being used by nlcp, so changing it there would make no effect.
  • The values that can be selected for the platform_data structure in the previous slide are shown below


/* Reference clock values */
enum {
	WL12XX_REFCLOCK_19	= 0, /* 19.2 MHz */
	WL12XX_REFCLOCK_26	= 1, /* 26 MHz */
	WL12XX_REFCLOCK_38	= 2, /* 38.4 MHz */
	WL12XX_REFCLOCK_52	= 3, /* 52 MHz */
	WL12XX_REFCLOCK_38_XTAL = 4, /* 38.4 MHz, XTAL */
	WL12XX_REFCLOCK_26_XTAL = 5, /* 26 MHz, XTAL */
};

/* TCXO clock values */
enum {
	WL12XX_TCXOCLOCK_19_2	= 0, /* 19.2MHz */
	WL12XX_TCXOCLOCK_26	= 1, /* 26 MHz */
	WL12XX_TCXOCLOCK_38_4	= 2, /* 38.4MHz */
	WL12XX_TCXOCLOCK_52	= 3, /* 52 MHz */
	WL12XX_TCXOCLOCK_16_368	= 4, /* 16.368 MHz */
	WL12XX_TCXOCLOCK_32_736	= 5, /* 32.736 MHz */
	WL12XX_TCXOCLOCK_16_8	= 6, /* 16.8 MHz */
	WL12XX_TCXOCLOCK_33_6	= 7, /* 33.6 MHz */
};


Platform Registration


  • After we have defined the platform data structures we need to register it with the kernel.
  • For am335x-evm this is done inside wl12xx_init() which calls wl12xx_set_platform_data that sets the platform data parameters that are later used by the wl1xxx driver.


static void wl12xx_init(int evm_id, int profile)
{
	struct device *dev;
	struct omap_mmc_platform_data *pdata;
	int ret;

...
...
	if (wl12xx_set_platform_data(&am335xevm_wlan_data))
		pr_err("error setting wl12xx data\n");

...
...

out:
	return;
}

Other Platform Links