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 includeHAL_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:
- 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.
- If the hardware has more than four SPI devices then additional chip selects are needed.
- 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:
-
The
qspi_qwr
field should be set toHAL_MCFxxxx_QSPIx_QWR_CSIV
, so the chip select is high when there is no I/O taking place. -
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). -
The QSPI_CS bits in the
qspi_qcr
field still have the usual meaning. -
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. -
When the I/O actually starts the
qspi_qcr
field will be used, deactivating all devices except the current one. - 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.
-
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.
2024-03-18 | eCosPro Non-Commercial Public License |