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.

TI81xx PSP Porting Guide

From Texas Instruments Wiki
Jump to: navigation, search
TIBanner.png
TI81xx PSP Porting Guide
PSP



NOTE:


  1. This document interchangeably uses DM816x and AM389x. TI816x refers to DM816x/AM389x devices unless specified otherwise.
  2. This document interchangeably uses DM814x and AM387x. TI814x refers to DM814x/AM387x devices unless specified otherwise.
  3. TI81xx refers to both DM816x and DM814x.

ReadMe First

The purpose of this document is to describe how to port the PSP releases for custom hardware where minimal changes are required in the kernel. This document DOES NOT describe how to add features to the drivers shipped in the PSP releases. Feature additions in the driver is outside the scope of this document. Driver features are typically listed in the PSP Data-Sheet (Performance Guide) or the respective Driver User Guides.

Prerequisites

  1. TI816x/TI814x Technical Reference Manual - Available on the TI website or with the local FAE.
  2. TI816x/TI814x EVM schematics - Available on the board vendor's website or with the local FAE.
  3. PSP release to be used as baseline for porting the drivers - Available on the software download page of the TI website or with the local FAE.

Background

TBD

Driver porting

I2C Driver

Enabling Additional Instances

DM8148 has four I2C instances. I2C1 is enabled from Linux side. HWMOD and clock data are already setup for all four I2C instances in the respective files.

Step 1: To add additional instance, arch/arm/mach-omap2/board-ti8148evm.c file needs to be modfied with additional defintions.

Step 2:

Modify arch/arm/mach-omap2/i2c.c to add correct mux entries.


     For example, for bus #3, following code needs to be added to function "omap2_i2c_mux_pins"
               if ((bus_id == 3) && cpu_is_ti814x()){
              sprintf(mux_name, "mcasp0_axr1.i2c3_scl");
              omap_mux_init_signal(mux_name, 0);
              sprintf(mux_name, "mcasp0_axr2.i2c3_sda");
              omap_mux_init_signal(mux_name, 0);
              return;
      }

For more details on setting up the mux, please refer to http://processors.wiki.ti.com/index.php/DM814x_C6A814x_AM387x_PSP_User_Guide#Modifying_Pin_Mux_settings

Step 3: Function to be modified - ti814x_evm_i2c_init(void)

Ex: for I2C1 instance,


  omap_register_i2c_bus(1, 100, ti814x_i2c_boardinfo,
                              ARRAY_SIZE(ti814x_i2c_boardinfo));

for I2C2 instance,

  omap_register_i2c_bus(2, 100, ti814x_i2c_boardinfo2,
                             ARRAY_SIZE(ti814x_i2c_boardinfo2)); 

whereas first parameter is the bus number, (I2C instance #), second parameter is the I2C bus frequency,3rd parameter is the data structure to the slav devices in the bus.

Adding additional slaves for custom boards

To add additional I2C slaves for the custom boards, following data structure need to be modified in arch/arm/mach-omap2/board-ti8148evm.c or board-ti8168evm.c


  static struct i2c_board_info __initdata ti816x_i2c_boardinfo0[] = {
      {
              I2C_BOARD_INFO("eeprom", 0x50),
              .platform_data  = &eeprom_info,
      },
      {
              I2C_BOARD_INFO("cpld", 0x23),
      },
      {
              I2C_BOARD_INFO("tlv320aic3x", 0x18),
      },
      {
              I2C_BOARD_INFO("IO Expander", 0x20),
      },
 };

Ethernet Driver - Adding Custom Ethernet Phy

Introduction

This discussion deals with the changes required to the Linux PSP Drivers when the Ethernet PHY device is changed to suit custom implementation

DM8168 Evaluation Module (EVM) Contains 2 instances of on board Ethernet GIGABIT PHY from LSI (ET1011C)

PHY driver support in Linux

PHY controller driver framework in Linux, presents a generic framework which can be used by the ethernet MAC drivers to easily interface with the PHY. Some of the drivers available are for PHY controllers from Broadcom, Realtek etc

A PHY driver is attached to a PHY device based on the match for PHY ID as read from the "PHY Identification Register" 1 and 2 of the PHY.

A "Generic PHY" driver is also available, which is attached dynamically when the on board PHY may not match the devices supported by the existing PHY drivers. This driver handles all basic functionality like PHY initialization, link advertisement/configuration, auto negotiation etc,


Usually the "Generic PHY" controller driver might be able to handle the PHY controller. However, if there are some configuration details that are not supported by the "Generic PHY" driver, then one may have to develop a custom PHY driver.


Custom PHY driver

PHY controller driver is required to provide a facility to interact with the PHY controller for initialization, link setup and change notification, and additionally some quirks or special handling required, if any, by the PHY.

Some cases that may require PHY handling

  • Special initialization and configuration sequence for a mode of operation
  • Special features that are to be used and require additional configuration
  • Special cases in interrupt handling
  • PHY controller contains silicon errata and needs a workaround for standard operation

One can refer to - drivers/net/phy/broadcom.c which is classic example of cases mentioned above.

  Note - The existing Linux PSP drivers use the "Generic PHY" driver but with an additional quirk. That is, the PHY needs to
 be configured to generate clock on the TX_CLK pin. This is required to maintain the clock while switching to and from 
 100 and 1000 MBps mode of operation. This part of initialization is not added as part of the "Generic PHY" driver and is 
 part of EMAC driver probe, as one time configuration.

Writing a custom PHY driver in Linux

The kernel documentation "Documenation/networking/phy.txt", explains this in section "Writing a PHY driver".

Including the driver in the build


  • Add entry into the "drivers/net/phy/Kconfig" under "MII PHY device drivers"
  • Edit the "drivers/net/phy/Makefile" to include compilation of the PHY driver file
  • Invoke kernel menuconfig to select the PHY driver to be built into the kernel

"Device Drivers"->"Network device support"->"PHY Device support and infrastructure" - here the custom PHY driver should be selected.

Miscellaneous details

While a PHY is populated on the board, its address on the MDIO bus is to be noted. This is to be supplied as a mask to the EMAC driver platform data for probing the PHY by the MDIO bus framework. For example, if the PHY address on the bus is 2 (in decimal), then this goes into the phy_mask of the emac_platform_data as 0x04 (bit position/weight represents the PHY address) refer to include/linux/davinci_emac.h, arch/arm/mach-omap2/devices.c and drivers/net/davinci_emac.c for details.


CPSW MII Phy

While a PHY is populated on the board, its address on the MDIO bus is to be noted. This is to be supplied as a mask to the CPSW driver platform data for probing the PHY by the MDIO bus framework. For example, if the PHY address on the bus is 2 (in decimal), then this goes into the phy_mask of the cpsw_mdio_device as 0x04 (bit position/weight represents the PHY address). Please refer arch/arm/mach-omap2/devices.c and drivers/net/cpsw.c

  • Pinmux configuration to support mii interface
  • Program GMII_SEL in control module with 0x0 for MII Interface
  • Reset Interface Control A of CPGMAC_SLx MAC Control Register to use MII Phy


CPSW RMII Phy

While a PHY is populated on the board, its address on the MDIO bus is to be noted. This is to be supplied as a mask to the CPSW driver platform data for probing the PHY by the MDIO bus framework. For example, if the PHY address on the bus is 2 (in decimal), then this goes into the phy_mask of the cpsw_mdio_device as 0x04 (bit position/weight represents the PHY address). Please refer arch/arm/mach-omap2/devices.c and drivers/net/cpsw.c

  • Pinmux configuration to support rmii interface
    • In u-Boot, this modification can be done in evm.c:cpsw_pad_config()
  • Phy ID setting in Platform data
    • In U-Boot Phy ID should be set in board/ti/<SOC>/evm.c:struct cpsw_slave_data cpsw_slaves[]
    • In kernel Phy ID should be set in arch/arm/mach-omap2/devices.c:struct cpsw_slave_data cpsw_slaves[]
  • Program GMII_SEL in control module with 0x5 for RMII Interface
    • In u-Boot, this modification can be done in evm.c:board_init()
  • ifctl_a in mac control register has to be enabled to control the RMII gasket. (Has to be done in releases 04.01.00.xx and 04.04.00.01)
  • When using external RMII ref clock, program the external clock source in RMII_REFCLK_SRC register.
    • In u-Boot, this modification can be done in evm.c:board_init()
  • Remove runtime phy id update in devices.c:ti814x_cpsw_init()


CPSW EMAC 1 bringup in uboot

Uboot currently supports only CPSW EMAC 0, to bring up EMAC 1 following steps needs to be ported.

  • In evm file:
    • Add pinmux for CPSW EMAC 1 in cpsw_pad_config()
    • Interchange the cpsw_slaves platform data.
  • In driver: make cpsw_get_slave_port to return CPSW port 2 offset ie "2"


PHY driver support in UBOOT

The guidelines - if a new PHY driver is required, as detailed above, holds good here too.


Writing a custom PHY driver in UBOOT

The custom PHY driver can be placed under cpu/arm926ejs/davinci/xyz_phy.c Refer to Intel lxt972.c or dp83848.c for examples. Edit "cpu/arm926ejs/davinci/Makefile" to include xyz_phy.c in build Edit "include/asm-arm/arch-xyz/emac_defs.h" to define the PHY config variable Refer to "include/asm-arm/arch-davinci/emac_defs.h" for example. This defines PHY_LXT972. Include the PHY registration in "drivers/net/davinci_emac.c". This is in davinci_emac_initialize()

Audio driver (McASP)

The base EVM provides audio functionality using McASP2 and AIC3106 codec. Please refer to the [TI81xx-PSP-Audio-Driver-User-Guide PSP Audio Driver User Guide ] for more information on this.

Board hookup

Before starting off, collect the following information from the docs listed in the prerequisites section

  1. No. of McASP instances on the device and the device specific info of each of the instances (eg: base address, IRQ no., EDMA event no., pin muxing etc)
  2. McASP instance used in PSP
  3. McASP instance to be used in custom hardware
  4. Codec used in PSP
  5. Codec used on the custom hardware
  6. McASP serializers used for establishing the path from the device (TI816/TI814x) to the codec in PSP
  7. McASP serializers used for establishing the path from the device (TI816/TI814x) to the codec in custom hardware
  8. I2C/SPI instance being used for controlling the codec in PSP
  9. I2C/SPI instance being used for controlling the codec in custom hardware

TI816x/TI814x use McASP2 (Serializer 0 for Tx and Serializer 1 for Rx) to communicate to the AIC3106 codec on the base EVM. The audio codec is controlled using I2C and uses I2C0 for this. If the custom hardware does not use I2C0, refer to the I2C driver section for the porting information.

This information gets reflected in the board file and devices file for the device in the kernel source code.

  1. TI816x: arch/arm/mach-omap2/board-ti8168evm.c and arch/arm/mach-omap2/devices.c
  2. TI814x: arch/arm/mach-omap2/board-ti8148evm.c and arch/arm/mach-omap2/devices.c
<kernel-src>/arch/arm/mach-omap2/board-ti8168evm.c

static void __init ti8168_evm_init(void)
{
      [...]
      ti816x_evm_i2c_init();
      [...]
      ti81xx_register_mcasp(0, &ti8168_evm_snd_data);
      ti816x_spi_init();
      [...]
}

Addition of another instance of McASP can be done by modifying the McASP2 related info shown below.

The information associated with McASP2 is represented in the following manner:

<kernel-src>/arch/arm/mach-omap2/board-ti8168evm.c

static u8 ti8168_iis_serializer_direction[] = {
       TX_MODE,        RX_MODE,        INACTIVE_MODE,  INACTIVE_MODE,
       INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,
       INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,
       INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,  INACTIVE_MODE,
};
static struct snd_platform_data ti8168_evm_snd_data = {
       .tx_dma_offset  = 0x46800000,
       .rx_dma_offset  = 0x46800000,
       .op_mode        = DAVINCI_MCASP_IIS_MODE,
       .num_serializer = ARRAY_SIZE(ti8168_iis_serializer_direction),
       .tdm_slots      = 2,
       .serial_dir     = ti8168_iis_serializer_direction,
       .asp_chan_q     = EVENTQ_2,
       .version        = MCASP_VERSION_2,
       .txnumevt       = 1,
       .rxnumevt       = 1,
};

Note that McASP2 of TI816x/TI814x does not require any pin muxing. If this is needed, refer to the section on pin-muxing. The pin muxing changes will go in the following function in the board file.

<kernel-src>/arch/arm/mach-omap2/board-ti8168evm.c

static struct omap_board_mux board_mux[] __initdata = {

      /* PIN mux for non-muxed NOR */
      TI816X_MUX(TIM7_OUT, OMAP_MUX_MODE1),   /* gpmc_a12 */
       TI816X_MUX(UART1_CTSN, OMAP_MUX_MODE1), /* gpmc_a13 */
       TI816X_MUX(UART1_RTSN, OMAP_MUX_MODE1), /* gpmc_a14 */
       TI816X_MUX(UART2_RTSN, OMAP_MUX_MODE1), /* gpmc_a15 */
       TI816X_MUX(SC1_RST, OMAP_MUX_MODE1),    /* gpmc_a15 TODO why 2 gpmc_a15 mux setting? */
       TI816X_MUX(UART2_CTSN, OMAP_MUX_MODE1), /* gpmc_a16 */
       TI816X_MUX(UART0_RIN, OMAP_MUX_MODE1),  /* gpmc_a17 */
       TI816X_MUX(UART0_DCDN, OMAP_MUX_MODE1), /* gpmc_a18 */
       TI816X_MUX(UART0_DSRN, OMAP_MUX_MODE1), /* gpmc_a19 */
       TI816X_MUX(UART0_DTRN, OMAP_MUX_MODE1), /* gpmc_a20 */
       TI816X_MUX(SPI_SCS3, OMAP_MUX_MODE1),   /* gpmc_a21 */
       TI816X_MUX(SPI_SCS2, OMAP_MUX_MODE1),   /* gpmc_a22 */
       TI816X_MUX(GP0_IO6, OMAP_MUX_MODE2),    /* gpmc_a23 */
       TI816X_MUX(TIM6_OUT, OMAP_MUX_MODE1),   /* gpmc-a24 */
       TI816X_MUX(SC0_DATA, OMAP_MUX_MODE1),   /* gpmc_a25 */
       TI816X_MUX(GPMC_A27, OMAP_MUX_MODE1),   /* gpio-20 for controlling high address */
       { .reg_offset = OMAP_MUX_TERMINATOR },
};
<kernel-src>/arch/arm/mach-omap2/devices.c

static struct resource ti81xx_mcasp_resource[] = {
       {
               .name = "mcasp",
               .start = TI81XX_ASP2_BASE,
               .end = TI81XX_ASP2_BASE + (SZ_1K * 12) - 1,
               .flags = IORESOURCE_MEM,
       },
       /* TX event */
       {
               .start = TI81XX_DMA_MCASP2_AXEVT,
               .end = TI81XX_DMA_MCASP2_AXEVT,
               .flags = IORESOURCE_DMA,
       },
       /* RX event */
       {
               .start = TI81XX_DMA_MCASP2_AREVT,
               .end = TI81XX_DMA_MCASP2_AREVT,
               .flags = IORESOURCE_DMA,
       },
};

static struct platform_device ti81xx_mcasp_device = {
       .name = "davinci-mcasp",
       .id = 2,
       .num_resources = ARRAY_SIZE(ti81xx_mcasp_resource),
       .resource = ti81xx_mcasp_resource,
};

void __init ti81xx_register_mcasp(int id, struct snd_platform_data *pdata)
{
       ti81xx_mcasp_device.dev.platform_data = pdata;
       platform_device_register(&ti81xx_mcasp_device);
}


Instead of making modifications to the McASP2 related info, it is possible to register another instance of McASP in the board file and use both the McASPs simultaneously. Doing this will require changes on the following lines:

  1. Rename ti8168_iis_serializer_direction to ti8168_iis_serializer_direction_mcasp2
  2. Add ti8168_iis_serializer_direction_mcasp<0/1> similar to ti8168_iis_serializer_direction_mcasp2
  3. Convert ti8168_evm_snd_data into an array and add info related to the other McASP
  4. Rename ti81xx_mcasp_resource to ti81xx_mcasp2_resource
  5. Add ti81xx_mcasp_resource<0/1> similar to ti81xx_mcasp2_resource
  6. Rename ti81xx_mcasp_device to ti81xx_mcasp2_device
  7. Add ti81xx_mcasp_device<0/1> similar to ti81xx_mcasp2_device
  8. Register McASP<0/1> device as platform device
  9. Other logical changes due to the renaming done in the previous steps

Please note that the steps listed above are meant to serve as a guideline only. It has not been verified on the EVMs.

Clocking related changes

McASP2 on the TI81xx EVMs operates in a slave mode (codec driving the bit clock and the frame sync) and hence nothing specific handling is needed.

ALSA framework hookup

In addition to make changes in the board file, additional changes might be necessary in the ASoC drivers. Platform and EVM specific hookups with the ALSA framework is done in the files are present under sound/soc/davinci/. The codec driver used for AIC3106 is sound/soc/codecs/tlv320aic3x.c

<kernel-src>/sound/soc/davinci/davinci-evm.c

static struct snd_soc_dai_link ti81xx_evm_dai = {
       .name = "TLV320AIC3X",
       .stream_name = "AIC3X",
       .cpu_dai_name= "davinci-mcasp.2",
       .codec_dai_name = "tlv320aic3x-hifi",
       .codec_name = "tlv320aic3x-codec.1-0018",
       .platform_name = "davinci-pcm-audio",
       .init = evm_aic3x_init,
       .ops = &evm_ops,
};

static struct snd_soc_card ti81xx_snd_soc_card = {
       .name = "TI81XX EVM",
       .dai_link = &ti81xx_evm_dai,
       .num_links = 1,
};

static int __init evm_init(void)
{
[...]
       } else if (machine_is_ti8168evm() || machine_is_ti8148evm()) {
               evm_snd_dev_data = &ti81xx_snd_soc_card;
               index = 0;
[...]
}

When the McASP instance or codec changes, modifications are to be done in ti81xx_evm_dai shown above.

Multi-McASP scenario

Note:The following changes have not been tested. The steps outlined below are meant to serve as a guideline only.

In order to have multiple McASPs working, extend the ti81xx_evm_dai struct on the following lines:

  1. Convert it to an array and extend it for supporting multiple instances.
  2. The number used in the cpu_dai_name indicates the McASP instance to be used
  3. The codec_name is made up of the name used in the codec driver alongwith the I2C bus and I2C address (1-0018 for the EVM).

If everything is fine, on kernel bootup you should see multiple sub-devices for the sound card.

Refer to the ALSA wiki http://alsa.opensrc.org/Main_Page for more information.

Refer to the kernel source code and Documentation/sound/* for more information about the various data structures and function used.

USB driver

Introduction

This section explains porting guidelines of USB module for TI81xx platform. The DM816X or DM814X platform consists of dual USB IP core based on Mentor Controller Graphics, shortly abbreviated as musb. The platform supports two musb controller musb0 and musb1 respectively and hence there are two independent usb bus/port. The musb driver source code is available in drivers/usb/musb directory. The musb core is integrated with CPPI4.1 complaint DMA which provides queue manager interface for queueing the usb transmit/receive request to/from DMA engine. There are 15 Tx/Rx CPPI4.1 DMA channels corresponding each Tx/Rx endpoint configuration for each musb controllers musb0/musb1 respectively. The USB-PHY is integrated within the SOCs and hence no external board specific configuration is required

For DM816X/DM814X platform following configuration is done as part of TI81xx kernel release.

  1. Adding musb devices for dual instance of musb controller
  2. Adding musb resources
  3. USB Clock support
  4. USB PHY initialization

The USB charge pump circuitry is external to SOC, which is auto driven by USB0_DRVVBUS or USB1_DRVVBUS signal from SOCs. There are no custom board specific configuration required in the usb driver except for enabling the usb support in custom board specifc file.

Enabling usb support in platform specific board file

The usb_musb_init() function is called from platform board specific initialization routine ti8168_evm_init()/ti8148_evm_init() from arch/arm/mach-omap2/board-ti8168evm.c or arch/arm/mach-omap2/board-ti8148evm.c files respectively. The musb_board_data is passed as parameter to the usb_musb_init() function.

    static void __init ti8168_evm_init(void)
   {
      ...
      /* initialize usb */
      usb_musb_init(&musb_board_data);
      ...
   }

The musb_board_data structure is defined as

   static struct omap_musb_board_data musb_board_data = {
      .interface_type         = MUSB_INTERFACE_ULPI,
  #ifdef CONFIG_USB_MUSB_OTG
      .mode           = MUSB_OTG,
  #elif defined(CONFIG_USB_MUSB_HDRC_HCD)
      .mode           = MUSB_HOST,
  #elif defined(CONFIG_USB_GADGET_MUSB_HDRC)
      .mode           = MUSB_PERIPHERAL,
  #endif
      .power                  = 500,
      .instances              = 1,
  };

The mode parameter is set to usb operation mode (HOST/PERIPHERAL/OTG). The number of musb instances can be set by initializing musb_board_data.instances member variable. By default one instance of musb controller is supported by setting the musb_board_data.instances to zero. To configure two instances of musb controller set musb_board_data.instances to 1.

About TI81xx CPPI4.1 DMA Driver

The CPPI4.1 consists of following components

  1. TX/RX DMA Channel
  2. Queue Manager
  3. DMA schedular

The ti81xx platform uses CPPI4.1 complaint DMA engine for transmit/reception of usb packets to/from mentor controller. The CPPI41 consists of 15 Tx/Rx DMA channel for each Tx/Rx endpoint of each musb controller. The Queue Manager responsible for queue/dequeue of usb transfer buffer descriptors (CPPI41 dma BDs) to/from queues. Each Tx DMA Channel reads the transmit BD from the two input queeus called tx-queues and push the completed BD to completed queue as configured in the Tx DMA channel configuration register. Similarly the Rx DMA channel takes the free rx-BDs from Rx free descriptor queues (configured in Rx DMA channel configuration registers) and fills the received packets into buffer descriptors, then pushes the completed received BDs into Rx-completion queues. The Tx/Rx completion queues are interrupting queue, interrupt will be generated whenever the DMA pushes any tx/rx completed BDs into these queues.

There is no custom board specific changes required for CPPI4.1 DMA driver.

MMC/SD Driver

Introduction

This discussion deals with the changes required to the Linux PSP Drivers when the MMC/SD module has to be changed to suit custom implementation.

DM8168 Evaluation Module (EVM) contains 1 instance of MMC/SD on base-board.

DM8148 Evaluation Module (EVM) contains 1 instance of MMC/SD (MMC/SD1) on base-board and 2 instances of MMC/SD (MMC/SD0 and MMC/SD2) on daughter card.


Adding support to enable multiple MMC/SD instances

This section explains the steps required to support multiple MMC/SD instances.

1. Pin Muxing

Configure all pins related to the MMC/SD instance to be enabled.


For eg.

The following pins are required to be configured for mmc0 in DM8148

mmc0_clk (PINCNTL8)

mmc0_cmd (PINCNTL9)

mmc0_dat0 (PINCNTL10)

mmc0_dat1 (PINCNTL11)

mmc0_dat2 (PINCNTL12)

mmc0_dat3 (PINCNTL13)

mmc0_dat4 (PINCNTL35)

mmc0_dat5 (PINCNTL36)

mmc0_dat6 (PINCNTL41)

mmc0_dat7 (PINCNTL42)

mmc0_sdcd (PINCNTL72)


2. Clocking

Figure out the clocking sequence that leads to the particular instance of MMC/SD to be enabled. Make sure that all the clocks in the sequence are active so that the MMC/SD instance gets the required clock.

The frequency range supported is as follows:

Minimum frequency: 400kHz

Maximum frequency: 52MHz

The frequency set is not hard-coded. Rather, the clock is decided by following a handshake mechanism between the card and the controller.


For eg.

The clocking sequence for mmc0 in DM8148 is as follows 20MHz OSC0 / 20-30Mhz OSC1 (VCXO optional off-chip) -> mainpll_oscclkmx -> DPLL_L3 (GS70 ADPLLLJ) -> dividers -> SYSCLK6 -> MMCHS0


3. PRCM register

The PRCM register set has a register that enables a particular MMC/SD instance. The value of this register should be set appropriately.


For eg.

CM_ALWON_MMCHS_0_CLKCTRL is a register in PRCM in DM8148. (Address Offset: 0x21C Physical address: 0x161C Instance: CM_ALWON Description: This register manages the MMCHS_0 clocks)

It's default value might be set to 0x00030000 (the '3' here means that the module is disabled and cannot be accessed)

Try writing 0x2 to this register (the '2' here would mean that the module is explicitly enabled. Interface clock (if not used for functions) may be gated according to the clock domain state. Functional clocks are guarantied to stay present. As long as in this configuration, power domain sleep transition cannot happen.)


4. Sample patch for board-file

The following patch adds support to enable multiple instances on AM3517 board:

diff --git a/arch/arm/mach-omap2/board-am3517evm.c b/arch/arm/mach-omap2/board-am3517evm.c
index 13ad60a..749c9dc 100644
--- a/arch/arm/mach-omap2/board-am3517evm.c
+++ b/arch/arm/mach-omap2/board-am3517evm.c
@@ -28,6 +28,7 @@
#include <linux/i2c/tsc2004.h>
#include <linux/input.h>
#include <linux/tca6416_keypad.h>
+#include <linux/mmc/host.h>
#include <mach/hardware.h>
#include <mach/am35xx.h>
@@ -45,6 +46,7 @@

#include "mux.h"
#include "control.h"
+#include "hsmmc.h"

#define AM35XX_EVM_MDIO_FREQUENCY      (1000000)

@@ -742,6 +744,23 @@ static void am3517_evm_hecc_init(struct ti_hecc_platform_data *pdata)
platform_device_register(&am3517_hecc_device);
}

+static struct omap2_hsmmc_info mmc[] = {
+       {
+               .mmc            = 1,
+               .caps           = MMC_CAP_4_BIT_DATA,
+               .gpio_cd        = 127,
+               .gpio_wp        = 126,
+       },
+       {
+               .mmc            = 2,
+               .caps           = MMC_CAP_4_BIT_DATA,
+               .gpio_cd        = 128,
+               .gpio_wp        = 129,
+       },
+       {}      /* Terminator */
+};
+
+
static void __init am3517_evm_init(void)
{
omap3_mux_init(board_mux, OMAP_PACKAGE_CBB);
@@ -784,6 +803,9 @@ static void __init am3517_evm_init(void)

/* Init TCA6416 keypad */
tca6416_keypad_init_irq();
+
+       /* MMC init function */
+       omap2_hsmmc_init(mmc);
}


Set ".gpio_cd" and ".gpio_wp" as "-EINVAL" in case of DM8148 due to dedicated pins for CD (card detect) and WP (write protect):

static struct omap2_hsmmc_info mmc[] = {
   {
           .mmc            = 1,
           .caps           = MMC_CAP_4_BIT_DATA,
           .gpio_cd        = -EINVAL,/* Dedicated pins for CD and WP */
           .gpio_wp        = -EINVAL,
           .ocr_mask       = MMC_VDD_33_34,
   },
   {}      /* Terminator */
};


5. Sample patch for DMA Support

To use MMC/SD instance 1 in DM8148, the following piece of code can be added:

diff --git a/arch/arm/plat-omap/include/plat/dma.h b/arch/arm/plat-omap/include/plat/dma.h 
index 32a79ca..6e02755 100644 
--- a/arch/arm/plat-omap/include/plat/dma.h 
+++ b/arch/arm/plat-omap/include/plat/dma.h 
@@ -184,6 +184,9 @@ 
#define OMAP24XX_DMA_SPI1_RX2          51      /* E_DMA_51 */ 
#define OMAP24XX_DMA_SPI1_TX3          52      /* E_DMA_52 */ 
#define OMAP24XX_DMA_SPI1_RX3          53      /* E_DMA_53 */ 
+ 
+#define OMAP24XX_DMA_MMC1_RX           3 
+#define OMAP24XX_DMA_MMC1_TX           2 
#endif


Similarly, to use any other instance (example MMC/SD0 or MMC/SD2 in DM8148), the DMA event information corresponding to that instance can be added.

Using polling functionality

This sample patch shows how to implement polling functionality for MMC/SD cards. The polling functionality is used as a work around to detect the insertion/removal of the MMC/SD card when the card detect pin is not connected.

Note: The board specific file is used to add the polling capabilty.

diff --git a/arch/arm/mach-omap2/board-dm385evm.c b/arch/arm/mach-omap2/board-dm385evm.c
index 4511c53..85f3896 100644
--- a/arch/arm/mach-omap2/board-dm385evm.c
+++ b/arch/arm/mach-omap2/board-dm385evm.c
@@ -64,7 +64,7 @@ static struct omap_board_mux board_mux[] __initdata = {
 static struct omap2_hsmmc_info mmc[] = {
        {
                .mmc            = 1,
-               .caps           = MMC_CAP_4_BIT_DATA,
+               .caps           = MMC_CAP_4_BIT_DATA | MMC_CAP_NEEDS_POLL,
                .gpio_cd        = -EINVAL, /* Dedicated pins for CD and WP */
                .gpio_wp        = -EINVAL,
                .ocr_mask       = MMC_VDD_33_34,
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index c348898..f2eaea2 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -584,7 +584,10 @@ static int omap_hsmmc_gpio_init(struct omap_mmc_platform_data *pdata)
                pdata->resume = omap_hsmmc_resume_cdirq;
                pdata->slots[0].get_cover_state = omap_hsmmc_get_cover_state;
                pdata->slots[0].get_ro = omap_hsmmc_get_wp;
-               pdata->slots[0].card_detect = omap_hsmmc_card_detect;
+               if (pdata->slots[0].caps & MMC_CAP_NEEDS_POLL)
+                       pdata->slots[0].card_detect = NULL;
+               else
+                       pdata->slots[0].card_detect = omap_hsmmc_card_detect;
        }

Changing transfer type

4-bit transfer type is selected as default. If the board in use can support 8-bit transfer type, the 8-bit transfer type capability can be used (as shown in the patch below).

Note: The board specific file is used to select the transfer type.

diff --git a/arch/arm/mach-omap2/board-ti8148evm.c b/arch/arm/mach-omap2/board-ti8148evm.c
index 8519842..369a496 100644
--- a/arch/arm/mach-omap2/board-ti8148evm.c
+++ b/arch/arm/mach-omap2/board-ti8148evm.c
@@ -66,7 +66,7 @@ static struct omap_board_mux board_mux[] __initdata = {
 static struct omap2_hsmmc_info mmc[] = {
        {
                .mmc            = 1,
-               .caps           = MMC_CAP_4_BIT_DATA,
+               .caps           = MMC_CAP_8_BIT_DATA,
                .gpio_cd        = -EINVAL, /* Dedicated pins for CD and WP */
                .gpio_wp        = -EINVAL,
                .ocr_mask       = MMC_VDD_33_34,

Using Hardware Timers from Kernel Module/Driver

DM816x devices have 7 h/w timers while DM814x devices have 8 timers. By default, the first two timers (labelled in kernel as Timer1 & Timer2) are reserved for kernel as periodic tick timer and a free running timer respectively.

This section describes steps for using one of the remaining timers from a loadable kernel module or driver.

Note that, depending upon the platform and use cases, some of the other timers may also be reserved. Please refer the Feature and Performance Guide Datasheet associated with respective release for more details.

Example: Using Timer5 from a loadable module

Note that for exact API details, please refer file arch/arm/plat-omap/include/plat/dmtimer.h from kernel source directory.

At bare minimum, the module has to make following dmtimer (the DM81xx timer driver in kernel) exported APIs in sequence:

  1. omap_dm_timer_request_specific() - Reserves the specific timer indicated by number as id - for DM816x, valid id is within 1 to 7 and for DM814x it is 1 to 8. If the timer was already reserved, then you will get error. On success, timer handle will be returned which can be used for further APIs.
  2. omap_dm_timer_request() - Alternatively, you can use this API to get the next free available timer. Note that, in this case it is assumed that every owner uses either of these APIs to reserve timers from kernel. On success, timer handle will be returned which can be used for further APIs.
  3. On DM814x, the default timer clock is at 32KHz while on DM816x, the default is main oscillator output. You can change the source to either by using API omap_dm_timer_set_source() and passing 0 (OMAP_TIMER_SRC_SYS_CLK) as argument to use oscillator clock output as timer clock or 1 (OMAP_TIMER_SRC_32_KHZ) to use 32KHz reference clock.
  4. Start the timer up counting using omap_dm_timer_set_load_start() by specifying load value.

Refer following patch which can go into a loadable kernel module to use Timer5 on DM814x:

 +#include <linux/err.h>
 +#include <linux/kernel.h>
 +#include <linux/module.h>
 +#include <linux/err.h>
 +#include <linux/device.h>
 +#include <linux/clk.h>
 +
 +#include <plat/dmtimer.h>
 +
 +static struct omap_dm_timer *timer5;
 +
 +static int __init init_timer5(void)
 +{
 +       int rate;
 +       unsigned int counter;
 +
 +       timer5 = omap_dm_timer_request_specific(5);
 +
 +       if(timer5 == NULL) {
 +               pr_err("Failed to reserve timer5\n");
 +               return -1;
 +       }
 +
 +       if (IS_ERR_VALUE(omap_dm_timer_set_source(timer5, OMAP_TIMER_SRC_SYS_CLK)))
 +               pr_err("Failed to set timer source\n");
 +
 +       /* Verify clock rate */
 +       rate = clk_get_rate(omap_dm_timer_get_fclk(timer5));
 +
 +       pr_info("Timer5 will run at at %u Hz\n", rate);
 +
 +       /* Start timer with 0 as load value and autoreload on overflow enabled */
 +       omap_dm_timer_set_load_start(timer5, 1, 0);
 +       pr_info("Timer5 is loaded with 0 and started up count...\n");
 +
 +       counter = omap_dm_timer_read_counter(timer5);
 +       pr_info("Current count = %u\n", counter);
 +
 +       return 0;
 +}
 +
 +static void __exit release_timer5(void)
 +{
 +       unsigned int counter;
 +
 +       counter = omap_dm_timer_read_counter(timer5);
 +       pr_info("Current count = %u\n", counter);
 +
 +       omap_dm_timer_free(timer5);
 +}
 +

Using different UART than the default EVM configuration as console

DM816x devices have 3 UARTs and DM814x has 6 instances. This section refers these UARTs starting from 0 (e.g., UART0, UART1 etc) while the code refers them with suffix starting from 1 (e.g., TI81XX_UART1 etc).

The default EVM configuration for respective devices enables and uses following UARTs as console:

  • DM8168 EVM: UART2 as console
  • DM8148 EVM: UART0 as console

In addition, the UART driver initializes all the other instances by default, but when using different UART as console, for example, on a custom board, pin multiplexing needs to be considered.

Following table lists multiplexing pins of various UARTs:

Note 1: By default, all multiplexed pins associated with UARTs are set in MODE0 (in case of DM816x, this is h/w default while for DM814x, the U-Boot takes case of this) and hence no mux setting is required for UARTs needing respective pins in MODE0, unless those pins are set to some other mode. In other words, the case where a driver or custom board setup for DM816x/Dm814x changes the mux mode associated with certain UART pins to non MODE0, the pins need to be set back to MODE0 for using those pins that same way as you would need to set up the pins not part of MODE0.

Note 2: Certain UART pins may be available as part of more than one pin, in this case, the decision to use certain pin would be board dependent.

Note 3: To refer the exact mux configuration and mode to be set for using as UART pin, refer datasheet and files arch/arm/mach-omap2/mux81xx.c and arch/arm/mach-omap2/mux814x.c for DM816x and DM814x respectively.

UART Multiplexed pin status on DM816x Multiplexed pin status on DM814x
UART0 All pins MODE0
All pins MODE0
UART1 All pins MODE0
RXD, TXD, CTS, RTS in non MODE0
UART2 All pins MODE0
RXD, TXD in non MODE0
UART3 N/A RXD, TXD, CTS, RTS in non MODE0
UART4 N/A RXD, TXD, CTS, RTS in non MODE0
UART5 N/A RXD, TXD, CTS, RTS, in non MODE0


The same considerations need to be taken into account even when using a UART through tty interface for some other purpose than as console from user space.

Example: Setting up UART3 on DM814x as console

In this example, we will only consider Tx & Rx pins on UART3 to be set up in mux and ignore h/w flow control.

Edit the board_mux array in arch/arm/mach-omap2/board-ti8148evm.c file (or any custom board file for DM814x board you are using) with following entries:

 static struct omap_board_mux board_mux[] __initdata = {
        TI814X_MUX(VOUT1_B_CB_C7, OMAP_MUX_MODE5), /* Set up pin as TXD */  
        TI814X_MUX(VOUT1_B_CB_C6, OMAP_MUX_MODE5), /* Set up pin as RXD */

        /* More entries for other peripheral pins .... */

        { .reg_offset = OMAP_MUX_TERMINATOR },
 };

Alternatively, the UART3 pins are also muxed with UART0 pins, so depending upon the routing on your board, you can set up to use UART0 pins as UART3 pins by:

 static struct omap_board_mux board_mux[] __initdata = {
        TI814X_MUX(UART0_DSRN, OMAP_MUX_MODE1), /* Set up pin as TXD */  
        TI814X_MUX(UART0_DCDN, OMAP_MUX_MODE1), /* Set up pin as RXD */

        /* More entries for other peripheral pins .... */

        { .reg_offset = OMAP_MUX_TERMINATOR },
 };

You could also use omap_mux_init_signal() to set up pinmux run time. Refer PSP User Guide section "Modifying_Pin_Mux_settings" for details.

Rebuild the kernel and now you can access the uart as /dev/ttyO3.

To use this UART for console, change 'console' parameter passed to bootargs as console=ttyO3,115200n8. Also ensure that the filesystem you are using is updated to use this device as console.

E.g., change the default boot arguments for DM814x at U-Boot from -->

'console=ttyO0,115200n8 root=/dev/nfs nfsroot=172.24.191.70:/srv/nfs/hp,nolock rw mem=128M ip=dhcp'

to following using setenv command,

TI8148_EVM#setenv bootargs 'console=ttyO3,115200n8 root=/dev/nfs nfsroot=172.24.191.70:/srv/nfs/hp,nolock rw mem=128M ip=dhcp'
TI8148_EVM#saveenv

Since on DM816x, no mux settings are required for UARTs if the pins are left in default MODE0, you can readily use the 'console' argument to kernel command like and update init scripts to use the UART other than UART2 (/dev/ttyO2) as console.

In the above example case, update the occurrences of 'ttyO0' in /etc/inittab of the DM814x filesystem you are using with 'ttyO3'.

That is, change

/sbin/getty 115200 ttyO0

in /etc/inittab to,

/sbin/getty 115200 ttyO3

Note: It is most likely that when using an UART as console you will update U-Boot too accordingly and thus the mux setting would already be taken care in U-Boot.

Low level debugging with early prints on console

If you wish to use an UART different than the one supported in default EVM config for low level debugging with early prints, you need to ensure that the pin mux is set up for the uart to be used before kernel boots (in U-Boot).

Also, following needs to be added in arch/arm/plat-omap/include/plat/uncompress.h. The example below assumes UART0 (referred in code as UART1) to be used as console with early debug on DM8168 board called as ti8168Cust.

Update function __arch_decomp_setup() with:

 /* TI8168 custom board using UART1 */
 DEBUG_LL_TI81XX(1, ti8168Cust);

Updating U-Boot to use different UART as console

For DM816x, change the address defined as CONFIG_SYS_NS16550_COM1 in include/configs/ti8168_evm.h to the base address of the UART you want to designate for console.

Similarly, for DM814x, update file include/configs/ti8148_evm.h.

In addition, if the chosen UART requires pinmux to be set (non-mode0 setting), this needs to be done in board_init() function.

For DM814x:

  1. Identify the mux configuration register number and the mode to be set for UART pin(s)
  2. Update the bit fields in board/ti/ti8148/mux.h accordingly

E.g., to set UART3 Tx (mode5) on vout1_b_cb_c7 pin, which comes to mux entry 211, change following in board/ti/ti8148/mux.h:

 /* -212 */      BIT(0), BIT(0), BIT(0), BIT(0),

to,

 /* -212 */      BIT(0), BIT(0), BIT(0), BIT(5),

SPI Port to Custom Hardware

http://processors.wiki.ti.com/index.php/DM81xx_AM38xx_PSP_McSPI_Driver_Guide#Porting_to_custom_hardware