Name

CYGPKG_DEVS_SPI_MCFxxxx_QSPI — eCos Support for the Freescale Coldfire QSPI Bus

Description

Several processors in the Freescale ColdFire family come with an on-chip SPI device, also known as QSPI. This package provides an eCos bus driver for that device. It implements the functionality defined by the generic SPI package CYGPKG_IO_SPI. The driver supports both polled and interrupt-driven transfers. Typical supported transfer rates range from 128KHz to 33MHz, although the exact details depend on the specific ColdFire processor being used and on the processor's clock speed. The hardware does not support DMA so large transfers at high transfer rates will consume much of the available cpu time.

This bus driver package does not instantiate any cyg_spi_bus structures. It is possible for a processor to have more than one SPI bus, so it is better to leave it to the processor HAL to define the bus or buses. Instead the bus driver package just provides functions and utility macros for use by the processor HAL. Similarly the bus driver package does not provide any cyg_spi_device structures. Exactly which devices are attached to the SPI bus is a characteristic of the platform so usually it is the platform HAL which provides the device instances.

Configuration Options

This SPI bus driver package should be loaded automatically when selecting a target containing a ColdFire processor with QSPI hardware, and it should never be necessary to load the package explicitly. If the application does not use any of the SPI functionality then all the SPI support code should be removed at link-time and the application does not suffer any overheads.

The package contains a single configuration option CYGHWR_DEVS_SPI_MCFxxxx_QSPI_MULTIPLE_BUSES. Usually this option should not be manipulated by application developers, instead it is set by the processor HAL. When the option is disabled the driver will optimize for the common case of a single bus.

The only other configuration options provided by this package relate to compiler flags.

Defining Buses

The header file cyg/io/mcfxxxx_qspi.h provides a utility macro CYG_MCFxxxx_QSPI_BUS to allow processor HALs to instantiate a bus. Existing HALs such as the MCF521x's will show how to use this macro.

Defining Devices

For most boards the platform HAL will create cyg_spi_device instances for all attached SPI devices, and will initialize the system so that the SPI-related processor pins are connected appropriately. Some development boards may not have any SPI devices but instead export the relevant signals to expansion connectors. In those cases it will be the responsibility of application code to create the device instances and manipulate the GPIO pins appropriately.

Device instances should take the form of a cyg_mcfxxxx_qspi_device structure, which contains a cyg_spi_device as its first field.

#include <cyg/io/mcfxxxx_qspi.h>
…
cyg_mcfxxxx_qspi_device hal_spi_atod CYG_SPI_DEVICE_ON_BUS(mcfxxxx_qspi) = {
    .qspi_common.spi_bus    = &cyg_mcfxxxx_qspi_bus,
    …
};

This defines a variable hal_spi_atod which can be used by other packages or by application code as an argument to the I/O functions provided by the generic SPI package CYGPKG_IO_SPI. A gcc extension, designated initializers, is used to fill in the qspi_common.spi_bus structure field. The structure contains a further seven fields which define exactly how to interact with the SPI device. Most of these fields are simply hardware register values, and the appropriate ColdFire User Manual should be consulted for full details of these registers. The header file cyg/hal/hal_io.h will provide #define's for the various bits, for example HAL_MCFxxxx_QSPIx_QMR_MSTR for the master mode bit of the QMR register.

qspi_qmr

When performing a transfer to this SPI device the bus driver will use the qspi_qmr field for the QSPI hardware's QMR register. The main fields in this register are:

MSTR
This bit specifies that the QSPI hardware should operate in master mode. It must always be set.
BITS
The data items transferred can range from 8 to 16 bits. For example, to specify 12-bit data items the qspi_qmr field should include HAL_MCFxxxx_QSPIx_QMR_BITS_12.
CPOL
Clock polarity. The default is inactive-low, active-high. If the device requires the opposite polarity then HAL_MCFxxxx_QSPIx_QMR_CPOL should be specified.
CPHA
Clock phase. The default is to capture data on the leading clock edge. If the device captures data on the trailing edge instead then HAL_MCFxxxx_QSPIx_QMR_CPHA should be specified.
BAUD
Baud rate divider. This should be a small number, usually between 1 and 255, which controls the clock rate. The value to be used depends on the device's maximum clock rate, the specific processor used, and the processor's clock speed.
qspi_qdlyr
This field is used to set the QSPI delay register QDLYR when performing transfers to this device. It contains two delay fields, QCD and DTL, which can be used in conjunction with qspi_qcr for fine control over bus timing. Most devices do not have any special requirements here so a value of 0 can be used. The register also contains an SPE bit to start a transfer, but that bit is used by the bus driver and should not be set in the device structure.
qspi_qwr
This field is used to set the QWR register. Only one bit, CSIV, in this register may be defined. The other fields in the register are manipulated by the bus driver. Usually if the device has an active-low chip select then the CSIV bit should be set, otherwise the structure field should be 0. If a custom chip select control function is used then that may require different CSIV behaviour.
qspi_qcr

This is used to fill in the command RAM registers during a data transfer. It contains five fields. The CONT bit is not normally required but can provide additional control over the chip select. Note that some versions of the various ColdFire User Manuals give an incomplete description of this bit and the errata sheets should be consulted as well. The BITSE bit should be set if transfers involve data items which are not 8 bits. The DT and DSCK bits can be used to enable one or both delays in the QDLYR register. The QSPI_CS field consists of four bits for the four QSPI chip select pins. If all the devices connected to the SPI bus are active-high and each is connected directly to a chip select, then only of these bits should be set. If all the devices are active-low then only one of the bits should be clear.

With some hardware the QSPI_CS bits can be more complicated. For example consider an SPI bus with an active-high device attached to QSPI chip selects 0 and 1, and active-low devices attached to the other two chip selects. The device definition for the CS0 device should have the QWR CSIV bit clear. The QCR QSPI_CS bits should have bits 0, 2 and 3 set. Between transfers all chip select pins will be low. This will activate the devices on CS2 and CS3, but since there is no clock signal this is harmless. When a transfer happens CS0, CS2 and CS3 will all be high, and CS1 will remain low. This will activate the device on CS0, but leave the other three devices inactive. Hence only the specified device is active during a transfer.

If the hardware requires further control over the chip selects then the device definition can include a custom qspi_cs_control function.

There is no support for using different QCR values for different parts of a transfer, for example the first data item versus the rest of the transfer. Such functionality is rarely useful and would require extra complexity in the bus driver, including performance-critical parts.

qspi_qcr_tick
This is used to fill in the command RAM registers during a tick operation, when none of the devices should be active. Some devices need to see a certain number of clock signals even when their chip select is not active, or they will not operate correctly. The hardware fields are the same as for qspi_qcr. Usually the QSPI_CS bits will be all 0 or all 1, but some hardware may require a more complicated value.
qspi_tick_data
When performing a tick operation this field will be used as the data to be transferred. Usually the value will not matter because, by the definition of an SPI tick, none of the SPI devices will be selected.
qspi_cs_control
Some hardware may have chip select requirements which cannot be satisfied simply by setting the QWR CSIV and the QCR QSPI_CS bits. For example if there are more than four SPI devices then the surplus may have their chip selects connected to GPIO pins. Also some devices may require that the chip select remain asserted for the duration of a multi-transfer transaction, and that is not supported directly by the QSPI hardware. To cope with such cases it is possible to define a custom chip select control function.

Consider a simple SPI device on a board with a 64MHz MCF5282 processor. The device uses 8-bit data, default clock polarity and phase, can be driven at up to 10 MHz, does not require any special delays, has an active-high chip select, and is connected to the processor's QSPI CS0 pin. There are no other devices on the bus.

#include <cyg/io/mcfxxxx_qspi.h>
…
cyg_mcfxxxx_qspi_device hal_spi_dev0 CYG_SPI_DEVICE_ON_BUS(mcfxxxx_qspi) = {
    .qspi_common.spi_bus    = &cyg_mcfxxxx_qspi_bus,
    .qspi_qmr               = HAL_MCFxxxx_QSPIx_QMR_MSTR |
                              HAL_MCFxxxx_QSPIx_QMR_BITS_8 |
                              0x04,
    .qspi_qdlyr             = 0,
    .qspi_qwr               = 0,
    .qspi_qcr               = HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS0,
    .qspi_qcr_tick          = 0,
    .qspi_tick_data         = 0xFF,
    .qspi_cs_control        = (void (*)(cyg_mcfxxxx_qspi_device*, int)) 0
};

For a more complicated example, consider a board with an MCF5272 processor and an SPI device that involves 12-bit data items, uses inverted clock polarity and phase, can only be driven at the slowest clock rate, does not require any special delays or chip select logic, has an active-low chip select, and is connected to the processor's QSPI CS2 pin:

#include <cyg/io/mcfxxxx_qspi.h>
…
cyg_mcfxxxx_qspi_device hal_spi_dev2 CYG_SPI_DEVICE_ON_BUS(mcfxxxx_qspi) = {
    .qspi_common.spi_bus    = &cyg_mcfxxxx_qspi_bus,
    .qspi_qmr               = HAL_MCFxxxx_QSPIx_QMR_MSTR |
                              HAL_MCFxxxx_QSPIx_QMR_BITS_12 |
                              HAL_MCFxxxx_QSPIx_QMR_CPOL |
                              HAL_MCFxxxx_QSPIx_QMR_CPHA |
                              0xFF,
    .qspi_qdlyr             = 0,
    .qspi_qwr               = HAL_MCFxxxx_QSPIx_QWR_CSIV,
    .qspi_qcr               = HAL_MCFxxxx_QSPIx_QCRn_BITSE |
                              HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS0 |
                              HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS1 |
                              HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS3,
    .qspi_qcr_tick          = HAL_MCFxxxx_QSPIx_QCRn_BITSE |
                              HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS0 |
                              HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS1 |
                              HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS2 |
                              HAL_MCFxxxx_QSPIx_QCRn_QSPI_CS_CS3,
    .qspi_tick_data         = 0xFF,
    .qspi_cs_control        = (void (*)(cyg_mcfxxxx_qspi_device*, int)) 0
};

This definition assumes that there are no attached SPI devices with an active-high chip select. If there are such devices then the qspi_qcr and qspi_qcr_tick fields should be modified so that these devices are not activated at the wrong time.

The header file cyg/io/mcfxxxx_qspi.h provides a utility macro CYG_MCFxxxx_QSPI_DEVICE which can be used to instantiate a device. Essentially the macro just expands to a structure definition as above.

Advanced Chip Select Control

The ColdFire QSPI hardware provides support for controlling the chip select signals of up to four SPI devices. In many situations this support is adequate, but there are exceptions:

  1. The QSPI chip select outputs may share processor pins with other on-chip ColdFire devices. For example on the mcf5272 the QSPI CS2 signal uses the same pin as the uart1 CTS signal, so if the application needs uart1 and hardware flow-control then that QSPI CS2 pin is no longer available.
  2. If the hardware has more than four SPI devices then additional chip selects are needed.
  3. With most SPI devices the chip select signal only needs to be asserted while I/O is taking place, but there are exceptions. For example interactions with an MMC card involve a sequence of transfers, and the chip select must remain asserted in between these transfers. Depending on thread priorities and other factors there may be a considerable delay between these transfers and the QSPI hardware does not provide any way of keeping a chip select asserted indefinitely.

The issue of insufficient chip selects can usually be handled by adding extra hardware, for example an external decoder chip possibly complemented by inverters if there is a mixture of active-high and active-low devices. This approach can be supported simply by programming the right values for qspi_qcr and qspi_qcr_tick, but the cost of the extra hardware may be unacceptable. An alternative approach is to use one or more of the processor's GPIO pins to control the extra devices.

The issue of persistent chip selects can be handled in one of two main ways. A GPIO pin can be used to control the chip select, bypassing the QSPI support. Alternatively the QWR CSIV bit can be used in an inverted sense, to activate an SPI device rather than to define the inactive state.

To support these variations an arbitrary chip select control function can be specified for a device. Such a function takes two arguments. The first is a pointer to the SPI device, possibly allowing the function to be shared between multiple devices. The second is one of the following:

CYG_MCFxxxx_QSPI_CS_INIT
During system initialization the QSPI bus driver will iterate over all the attached SPI devices. If a device has a qspi_cs_control function then this will be invoked. A typical action would be to configure a GPIO pin. Note that these calls happen quite early during system initialization so other subsystems like standard I/O may not be set up yet.
CYG_MCFxxxx_QSPI_CS_ASSERT
This is used to assert the chip select, in other words to set the chip select to low for an active-low device or high for an active-high device. It will be called at the start of any transfer, unless the previous transfer has left the chip select asserted.
CYG_MCFxxxx_QSPI_CS_DROP
This is used to deassert the chip select. It will be called at the end of any transfer that specifies drop_cs. It will also be called at the start of a tick operation.

To support persistent chip selects via the CSIV signal the bus driver package provides two chip control functions cyg_mcfxxxx_qspi_csiv_cs_control_active_high and cyg_mcfxxxx_qspi_csiv_cs_control_active_low. To use these with say an active-low device:

  1. The qspi_qwr field should be set to HAL_MCFxxxx_QSPIx_QWR_CSIV, so the chip select is high when there is no I/O taking place.
  2. The qspi_cs_control field should be set to &cyg_mcfxxxx_qspi_csiv_cs_control_active_low. This function will be invoked by the bus driver to assert or drop the signal (initialization is a no-op).
  3. The QSPI_CS bits in the qspi_qcr field still have the usual meaning.
  4. At the start of a transfer cyg_mcfxxxx_qspi_csiv_cs_control_active_low will clear the QWR CSIV bit. There is no I/O taking place yet so all chip select outputs will switch to low, activating all active-low devices. This is generally harmless since there is no clock signal.
  5. When the I/O actually starts the qspi_qcr field will be used, deactivating all devices except the current one.
  6. At the end of each individual transfer the chip selects will revert to their inactive state, which because of the CSIV setting means low. Again this will activate all active-low devices, but there is no clock signal so no I/O takes place.
  7. For the last transfer of a transaction or for a tick operation cyg_mcfxxxx_qspi_cs_control_active_low will be invoked again with a DROP argument. It will reset the QWR CSIV bit to 1, deactivating all devices.

The overall effect is a persistent chip select with the desired polarity, using just the QSPI hardware facilities rather than a GPIO pin.