Mercurial > ecos
changeset 1733:77d1baeea166
Add generic SPI support
| author | bartv |
|---|---|
| date | Tue, 24 Aug 2004 17:31:16 +0000 |
| parents | 171cd3a5df5d |
| children | f652cc8f54ed |
| files | packages/ChangeLog packages/ecos.db packages/io/spi/current/ChangeLog packages/io/spi/current/cdl/spi.cdl packages/io/spi/current/doc/spi.sgml packages/io/spi/current/doc/spi_net.fig packages/io/spi/current/doc/spi_net.png packages/io/spi/current/include/spi.h packages/io/spi/current/src/spi.c |
| diffstat | 9 files changed, 1243 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,7 @@ +2004-08-24 Bart Veer <bartv@ecoscentric.com> + + * ecos.db: add SPI package + 2004-08-06 Jani Monoses <jani@iv.ro> * ecos.db: Remove two obsolete flash drivers: INTEL_BOOTBLOCK
--- a/packages/ecos.db +++ b/packages/ecos.db @@ -2003,6 +2003,16 @@ package CYGPKG_IO_WATCHDOG { This package provides a watchdog device." } +package CYGPKG_IO_SPI { + alias { "Generic SPI support" spi io_spi spi_io } + directory io/spi + script spi.cdl + description " + The generic SPI package provides an API for accessing devices + attached to an SPI bus. It also provides support for writing + bus drivers and for defining SPI device structures." +} + package CYGPKG_KERNEL { alias { "eCos kernel" kernel } directory kernel
new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/ChangeLog @@ -0,0 +1,35 @@ +2004-04-23 Bart Veer <bartv@ecoscentric.com> + + * Generic SPI package created + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//===========================================================================
new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/cdl/spi.cdl @@ -0,0 +1,90 @@ +# ==================================================================== +# +# spi.cdl +# +# Generic SPI package configuration data +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2004 eCosCentric Limited +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): bartv +# Date: 2004-04-24 +# +#####DESCRIPTIONEND#### +#======================================================================== + +cdl_package CYGPKG_IO_SPI { + display "Generic SPI support" + requires CYGPKG_INFRA CYGPKG_HAL + hardware + include_dir cyg/io + compile spi.c + + description " + The generic SPI package provides an API for accessing devices + attached to an SPI bus. It also provides support for writing + bus drivers and for defining SPI device structures." + + + cdl_component CYGPKG_IO_SPI_OPTIONS { + display "SPI build options" + flavor none + description " + Package specific build options including control over + compiler flags used only in building this package, + and details of which tests are built." + + + cdl_option CYGPKG_IO_SPI_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the serial device drivers. These flags are used in addition + to the set of global flags." + } + + cdl_option CYGPKG_IO_SPI_CFLAGS_REMOVE { + display "Suppressed compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the serial device drivers. These flags are removed from + the set of global flags if present." + } + } +}
new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/doc/spi.sgml @@ -0,0 +1,711 @@ +<!-- DOCTYPE part PUBLIC "-//OASIS//DTD DocBook V3.1//EN" --> + +<!-- {{{ Banner --> + +<!-- =============================================================== --> +<!-- --> +<!-- spi.sgml --> +<!-- --> +<!-- Generic SPI documentation. --> +<!-- --> +<!-- =============================================================== --> +<!-- ####COPYRIGHTBEGIN#### --> +<!-- --> +<!-- =============================================================== --> +<!-- Copyright (C) 2004 eCosCentric Limited --> +<!-- This material may be distributed only subject to the terms --> +<!-- and conditions set forth in the Open Publication License, v1.0 --> +<!-- or later (the latest version is presently available at --> +<!-- http://www.opencontent.org/openpub/) --> +<!-- Distribution of the work or derivative of the work in any --> +<!-- standard (paper) book form is prohibited unless prior --> +<!-- permission obtained from the copyright holder --> +<!-- =============================================================== --> +<!-- --> +<!-- ####COPYRIGHTEND#### --> +<!-- =============================================================== --> +<!-- #####DESCRIPTIONBEGIN#### --> +<!-- --> +<!-- Author(s): bartv --> +<!-- Date: 2004/04/23 --> +<!-- --> +<!-- ####DESCRIPTIONEND#### --> +<!-- =============================================================== --> + +<!-- }}} --> + +<part id="io-spi"><title>SPI Support</title> + +<refentry id="spi"> + <refmeta> + <refentrytitle>Overview</refentrytitle> + </refmeta> + <refnamediv> + <refname>Overview</refname> + <refpurpose>eCos Support for SPI, the Serial Peripheral Interface</refpurpose> + </refnamediv> + + <refsect1 id="spi-description"><title>Description</title> + <para> +The Serial Peripheral Interface (SPI) is one of a number of serial bus +technologies. It can be used to connect a processor to one or more +peripheral chips, for example analog-to-digital convertors or real +time clocks, using only a small number of pins and PCB tracks. The +technology was originally developed by Motorola but is now also +supported by other vendors. + </para> + <para> +A typical SPI system might look like this: + </para> + <informalfigure PgWide=1> + <mediaobject> + <imageobject> + <imagedata fileref="spi_net.png" Scalefit=1 Align="Center"> + </imageobject> + </mediaobject> + </informalfigure> + <para> +At the start of a data transfer the master cpu asserts one of the chip +select signals and then generates a clock signal. During each clock +tick the cpu will output one bit on its master-out-slave-in line and +read one bit on the master-in-slave-out line. Each device is connected +to the clock line, the two data lines, and has its own chip select. If +a device's chip select is not asserted then it will ignore any +incoming data and will tristate its output. If a device's chip select +is asserted then during each clock tick it will read one bit of data +on its input pin and output one bit on its output pin. + </para> + <para> +The net effect is that the cpu can write an arbitrary amount of data +to one of the attached devices at a time, and simultaneously read the +same amount of data. Some devices are inherently uni-directional. For +example an LED unit would only accept data from the cpu: it will not +send anything meaningful back; the cpu will still sample its input +every clock tick, but this should be discarded. + </para> + <para> +A useful feature of SPI is that there is no flow control from the +device back to the cpu. If the cpu tries to communicate with a device +that is not currently present, for example an MMC socket which does +not contain a card, then the I/O will still proceed. However the cpu +will read random data. Typically software-level CRC checksums or +similar techniques will be used to allow the cpu to detect this. + </para> + <para> +SPI communication is not fully standardized. Variations between +devices include the following: + </para> + <orderedlist> + <listitem><para> +Many devices involve byte transfers, where the unit of data is 8 bits. +Others use larger units, up to 16 bits. + </para></listitem> + <listitem><para> +Chip selects may be active-high or active-low. If the attached devices +use a mixture of polarities then this can complicate things. + </para></listitem> + <listitem><para> +Clock rates can vary from 128KHz to 20MHz or greater. With some +devices it is necessary to interrogate the device using a slow clock, +then use the obtained information to select a faster clock for +subsequent transfers. + </para></listitem> + <listitem><para> +The clock is inactive between data transfers. When inactive the +clock's polarity can be high or low. + </para></listitem> + <listitem><para> +Devices depend on the phase of the clock. Data may be sampled on +either the rising edge or the falling edge of the clock. + </para></listitem> + <listitem><para> +A device may need additional delays, for example between asserting +the chip select and the first clock tick. + </para></listitem> + <listitem><para> +Some devices involve complicated transactions: perhaps a command from +cpu to device; then an initial status response from the device; a data +transfer; and a final status response. From the cpu's perspective +these are separate stages and it may be necessary to abort the +operation after the initial status response. However the device may +require that the chip select remain asserted for the whole +transaction. A side effect of this is that it is not possible to do a +quick transfer with another device in the middle of the transaction. + </para></listitem> + <listitem><para> +Certain devices, for example MMC cards, depend on a clock signal after +a transfer has completed and the chip select has dropped. This clock +is used to finish some processing within the device. + </para></listitem> + </orderedlist> + <para> +Inside the cpu the clock and data signals are usually managed by +dedicated hardware. Alternatively SPI can be implemented using +bit-banging, but that approach is normally used for other serial bus +technologies such as I2C. The chip selects may also be implemented by +the dedicated SPI hardware, but often GPIO pins are used instead. + </para> + </refsect1> + + <refsect1 id="spi-ecos-implementation"><title>eCos Support for SPI</title> + <para> +The eCos SPI support for any given platform is spread over a number of +different packages: + </para> + <itemizedlist> + <listitem><para> +This package, <varname>CYGPKG_IO_SPI</varname>, exports an API for +accessing devices attached to an SPI bus. This API handles issues such +as locking between threads. The package does not contain any +hardware-specific code, instead it will call into an SPI bus driver +package. + </para> + <para> +In future this package may be extended with a bit-banging +implementation of an SPI bus driver. This would depend on lower-level +code for manipulating the GPIO pins used for the clock, data and chip +select signals, but timing and framing could be handled by generic code. + </para></listitem> + <listitem><para> +There will be a bus driver package for the specific SPI hardware on +the target hardware, for example +<varname>CYGPKG_DEVS_SPI_MCF52xx_QSPI</varname>. This is responsible +for the actual I/O. A bus driver may be used on many different boards, +all with the same SPI bus but with different devices attached to that +bus. Details of the actual devices should be supplied by other code. + </para></listitem> + <listitem><para> +The generic API depends on <structname>cyg_spi_device</structname> +data structures. These contain the information needed by a bus driver, +for example the appropriate clock rate and the chip select to use. +Usually the data structures are provided by the platform HAL since it +is that package which knows about all the devices on the board. + </para> + <para> +On some development boards the SPI pins are brought out to expansion +connectors, allowing end users to add extra devices. In such cases the +platform HAL may not know about all the devices on the board. Data +structures for the additional devices can instead be supplied by +application code. + </para></listitem> + <listitem><para> +Some types of SPI devices may have their own driver package. For +example one common use for SPI buses is to provide low-cost +MultiMediaCard (MMC) support. An MMC is a non-trivial device so there +is an eCos package specially for that, providing a block device +interface for higher-level code such as file systems. Other SPI +devices such as analog-to-digital converters are much simpler and +come in many varieties. There are no dedicated packages to support +each such device: the chances are low that another board would use the +exact same device, so there are no opportunities for code re-use. +Instead the devices may be accessed directly by application code or by +extra functions in the platform HAL. + </para></listitem> + </itemizedlist> + <para> +Typically all appropriate packages will be loaded automatically when +you configure eCos for a given target. If the application does not use +of the SPI I/O facilities, directly or indirectly, then linker garbage +collection should eliminate all unnecessary code and data. All +necessary initialization should happen automatically. However the +exact details may depend on the target, so the platform HAL +documentation should be checked for further details. + </para> + <para> +There is one important exception to this: if the SPI devices are +attached to an expansion connector then the platform HAL will not know +about these devices. Instead more work will have to be done by +application code. + </para> + </refsect1> + +</refentry> + +<refentry id="spi-api"> + <refmeta> + <refentrytitle>SPI Interface</refentrytitle> + </refmeta> + <refnamediv> + <refname>SPI Functions</refname> + <refpurpose>allow applications and other packages to access SPI devices</refpurpose> + </refnamediv> + <refsynopsisdiv> + <funcsynopsis> + <funcsynopsisinfo> +#include <cyg/io/spi.h> + </funcsynopsisinfo> + <funcprototype> + <funcdef>void <function>cyg_spi_transfer</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + <paramdef>cyg_bool <parameter>polled</parameter></paramdef> + <paramdef>cyg_uint32 <parameter>count</parameter></paramdef> + <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef> + <paramdef>cyg_uint8* <parameter>rx_data</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>void <function>cyg_spi_tick</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + <paramdef>cyg_bool <parameter>polled</parameter></paramdef> + <paramdef>cyg_uint32 <parameter>count</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_spi_get_config</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + <paramdef>cyg_uint32 <parameter>key</parameter></paramdef> + <paramdef>void* <parameter>buf</parameter></paramdef> + <paramdef>cyg_uint32* <parameter>len</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>int <function>cyg_spi_set_config</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + <paramdef>cyg_uint32 <parameter>key</parameter></paramdef> + <paramdef>const void* <parameter>buf</parameter></paramdef> + <paramdef>cyg_uint32* <parameter>len</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>void <function>cyg_spi_transaction_begin</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>cyg_bool <function>cyg_spi_transaction_begin_nb</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>void <function>cyg_spi_transaction_transfer</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + <paramdef>cyg_bool <parameter>polled</parameter></paramdef> + <paramdef>cyg_uint32 <parameter>count</parameter></paramdef> + <paramdef>const cyg_uint8* <parameter>tx_data</parameter></paramdef> + <paramdef>cyg_uint8* <parameter>rx_data</parameter></paramdef> + <paramdef>cyg_bool <parameter>drop_cs</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>void <function>cyg_spi_transaction_tick</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + <paramdef>cyg_bool <parameter>polled</parameter></paramdef> + <paramdef>cyg_uint32 <parameter>count</parameter></paramdef> + </funcprototype> + <funcprototype> + <funcdef>void <function>cyg_spi_transaction_end</function></funcdef> + <paramdef>cyg_spi_device* <parameter>device</parameter></paramdef> + </funcprototype> + </funcsynopsis> + </refsynopsisdiv> + + <refsect1 id="spi-api-description"><title>Description</title> + <para> +All SPI functions take a pointer to a +<structname>cyg_spi_device</structname> structure as their first +argument. This is an opaque data structure, usually provided by the +platform HAL. It contains the information needed by the SPI bus driver +to interact with the device, for example the required clock rate and +polarity. + </para> + <para> +An SPI transfer involves the following stages: + </para> + <orderedlist> + <listitem><para> +Perform thread-level locking on the bus. Only one thread at a time is +allowed to access an SPI bus. This eliminates the need to worry about +locking at the bus driver level. If a platform involves multiple SPI +buses then each one will have its own lock. Prepare the bus for +transfers to the specified device, for example by making sure it will +tick at the right clock rate. + </para></listitem> + <listitem><para> +Assert the chip select on the specified device, then transfer data to +and from the device. There may be a single data transfer or a +sequence. It may or may not be necessary to keep the chip select +asserted throughout a sequence. + </para></listitem> + <listitem><para> +Optionally generate some number of clock ticks without asserting a +chip select, for those devices which need this to complete an +operation. + </para></listitem> + <listitem><para> +Return the bus to a quiescent state. Then unlock the bus, allowing +other threads to perform SPI operations on devices attached to this +bus. + </para></listitem> + </orderedlist> + <para> +The simple functions <function>cyg_spi_transfer</function> and +<function>cyg_spi_tick</function> perform all these steps in a single +call. These are suitable for simple I/O operations. The alternative +transaction-oriented functions each perform just one of these steps. +This makes it possible to perform multiple transfers while only +locking and unlocking the bus once, as required for more complicated +devices. + </para> + <para> +With the exception of +<function>cyg_spi_transaction_begin_nb</function> all the functions +will block until completion. There are no error conditions. An SPI +transfer will always take a predictable amount of time, depending on +the transfer size and the clock rate. The SPI bus does not receive any +feedback from a device about possible errors, instead those have to be +handled by software at a higher level. If a thread cannot afford the +time it will take to perform a complete large transfer then a number +of smaller transfers can be used instead. + </para> + <para> +SPI operations should always be performed at thread-level or during +system initialization, and not inside an ISR or DSR. This greatly +simplifies locking. Also a typical ISR or DSR should not perform a +blocking operation such as an SPI transfer. + </para> + <para> +SPI transfers can happen in either polled or interrupt-driven mode. +Typically polled mode should be used during system initialization, +before the scheduler has been started and interrupts have been +enabled. Polled mode should also be used in single-threaded +applications such as RedBoot. A typical multi-threaded application +should normally use interrupt-driven mode because this allows for more +efficient use of cpu cycles. Polled mode may be used in a +multi-threaded application but this is generally undesirable: the cpu +will spin while waiting for a transfer to complete, wasting cycles; +also the current thread may get preempted or timesliced, making the +timing of an SPI transfer much less predictable. On some hardware +interrupt-driven mode is impossible or would be very inefficient. In +such cases the bus drivers will only support polled mode and will +ignore the <varname>polled</varname> argument. + </para> + </refsect1> + + <refsect1 id="spi-api-transfer"><title>Simple Transfers</title> + <para> +<function>cyg_spi_transfer</function> can be used for SPI operations +to simple devices. It takes the following arguments: + </para> + <variablelist> + <varlistentry> + <term><type>cyg_spi_device*</type> <varname>device</varname></term> + <listitem><para> +This identifies the SPI device that should be used. + </para></listitem> + </varlistentry> + <varlistentry> + <term><type>cyg_bool</type> <varname>polled</varname></term> + <listitem><para> +Polled mode should be used during system initialization and in a +single-threaded application. Interrupt-driven mode should normally be +used in a multi-threaded application. + </para></listitem> + </varlistentry> + <varlistentry> + <term><type>cyg_uint32</type> <varname>count</varname></term> + <listitem><para> +This identifies the number of data items to be transferred. Usually +each data item is a single byte, but some devices use a larger size up +to 16 bits. + </para></listitem> + </varlistentry> + <varlistentry> + <term><type>const cyg_uint8*</type> <varname>tx_data</varname></term> + <listitem><para> +The data to be transferred to the device. If the device will only +output data and ignore its input then a null pointer can be used. +Otherwise the array should contain <varname>count</varname> data +items, usually bytes. For devices where each data item is larger than +one byte the argument will be interpreted as an array of shorts +instead, and should be aligned to a 2-byte boundary. The bottom n bits +of each short will be sent to the device. The buffer need not be +aligned to a cache-line boundary, even for SPI devices which use DMA +transfers, but some bus drivers may provide better performance if the +buffer is suitably aligned. The buffer will not be modified by the +transfer. + </para></listitem> + </varlistentry> + <varlistentry> + <term><type>cyg_uint8*</type> <varname>rx_data</varname></term> + <listitem><para> +A buffer for the data to be received from the device. If the device +does not generate any output then a null pointer can be used. +The same size and alignment rules apply as for <varname>tx_data</varname>. + </para></listitem> + </varlistentry> + </variablelist> + <para> +<function>cyg_spi_transfer</function> performs all the stages of an +SPI transfer: locking the bus; setting it up correctly for the +specified device; asserting the chip select and transferring the data; +dropping the chip select at the end of the transfer; returning the bus +to a quiescent state; and unlocking the bus. + </para> + </refsect1> + + <refsect1 id="spi-api-tick"><title>Additional Clock Ticks</title> + <para> +Some devices require a number of clock ticks on the SPI bus between +transfers so that they can complete some internal processing. These +ticks must happen at the appropriate clock rate but no chip select +should be asserted and no data transfer will happen. +<function>cyg_spi_tick</function> provides this functionality. +The <varname>device</varname> argument identifies the SPI bus, the +required clock rate and the size of each data item. The +<varname>polled</varname> argument has the usual meaning. The +<varname>count</varname> argument specifies the number of data items +that would be transferred, which in conjunction with the size of each +data item determines the number of clock ticks. + </para> + </refsect1> + + <refsect1 id="spi-api-transaction"><title>Transactions</title> + <para> +A transaction-oriented API is available for interacting with more +complicated devices. This provides separate functions for each of the +steps in an SPI transfer. + </para> + <para> +<function>cyg_spi_transaction_begin</function> must be used at the +start of a transaction. This performs thread-level locking on the bus, +blocking if it is currently in use by another thread. Then it prepares +the bus for transfers to the specified device, for example by making +sure it will tick at the right clock rate. + </para> + <para> +<function>cyg_spi_transaction_begin_nb</function> is a non-blocking +variant, useful for threads which cannot afford to block for an +indefinite period. If the bus is currently locked the function returns +false immediately. If the bus is not locked then it acts as +<filename>cyg_spi_transaction_begin</filename> and returns true. + </para> + <para> +Once the bus has been locked it is possible to perform one or more +data transfers by calling +<function>cyg_spi_transaction_transfer</function>. This takes the same +arguments as <function>cyg_spi_transfer</function>, plus an additional +one <varname>drop_cs</varname>. A non-zero value specifies that +the device's chip select should be dropped at the end of the transfer, +otherwise the chip select remains asserted. It is essential that the +chip select be dropped in the final transfer of a transaction. If the +protocol makes this difficult then +<function>cyg_spi_transaction_tick</function> can be used to generate +dummy ticks with all chip selects dropped. + </para> + <para> +If the device requires additional clock ticks in the middle of a +transaction without being selected, +<function>cyg_spi_transaction_tick</function> can be used. This will +drop the device's chip select if necessary, then generate the +appropriate number of ticks. The arguments are the same as for +<function>cyg_spi_tick</function>. + </para> + <para> +<function>cyg_spi_transaction_end</function> should be called at the +end of a transaction. It returns the SPI bus to a quiescent state, +then unlocks it so that other threads can perform I/O. + </para> + <para> +A typical transaction might involve the following. First a command +should be sent to the device, consisting of four bytes. The device +will then respond with a single status byte, zero for failure, +non-zero for success. If successful then the device can accept another +n bytes of data, and will generate a 2-byte response including a +checksum. The device's chip select should remain asserted throughout. +The code for this would look something like: + </para> + <programlisting> +#include <cyg/io/spi.h> +#include <cyg/hal/hal_io.h> // Defines the SPI devices +… + cyg_spi_transaction_begin(&hal_spi_eprom); + // Interrupt-driven transfer, four bytes of command + cyg_spi_transaction_transfer(&hal_spi_eprom, 0, 4, command, NULL, 0); + // Read back the status + cyg_spi_transaction_transfer(&hal_spi_eprom, 0, 1, NULL, status, 0); + if (!status[0]) { + // Command failed, generate some extra ticks to drop the chip select + cyg_spi_transaction_tick(&hal_spi_eprom, 0, 1); + } else { + // Transfer the data, then read back the final status. The + // chip select should be dropped at the end of this. + cyg_spi_transaction_transfer(&hal_spi_eprom, 0, n, data, NULL, 0); + cyg_spi_transaction_transfer(&hal_spi_eprom, 0, 2, NULL, status, 1); + // Code for checking the final status should go here + } + // Transaction complete so clean up + cyg_spi_transaction_end(&hal_spi_eprom); + </programlisting> + <para> +A number of variations are possible. For example the command and +status could be packed into the beginning and end of two 5-byte +arrays, allowing a single transfer. + </para> + </refsect1> + + <refsect1 id="spi-api-config"><title>Device Configuration</title> + <para> +The functions <function>cyg_spi_get_config</function> and +<function>cyg_spi_set_config</function> can be used to examine and +change parameters associated with SPI transfers. The only keys that +are defined for all devices are +<varname>CYG_IO_GET_CONFIG_SPI_CLOCKRATE</varname> and +<varname>CYG_IO_SET_CONFIG_SPI_CLOCKRATE</varname>. Some types of +device, for example MMC cards, support a range of clock rates. The +<structname>cyg_spi_device</structname> structure will be initialized +with a low clock rate. During system initialization the device will be +queried for the optimal clock rate, and the +<structname>cyg_spi_device</structname> should then be updated. The +argument should be a clock rate in Hertz. For example the following +code switches communication to 1Mbit/s: + </para> + <programlisting> + cyg_uint32 new_clock_rate = 1000000; + cyg_uint32 len = sizeof(cyg_uint32); + if (cyg_spi_set_config(&hal_mmc_device, + CYG_IO_SET_CONFIG_SPI_CLOCKRATE, + (const void *)&new_clock_rate, &len)) { + // Error recovery code + } + </programlisting> + <para> +If an SPI bus driver does not support the exact clock rate specified +it will normally use the nearest valid one. SPI bus drivers may define +additional keys appropriate for specific hardware. This means that the +valid keys are not known by the generic code, and theoretically it is +possible to use a key that is not valid for the SPI bus to which the +device is attached. It is also possible that the argument used with +one of these keys is invalid. Hence both +<function>cyg_spi_get_config</function> and +<function>cyg_spi_set_config</function> can return error codes. The +return value will be 0 for success, non-zero for failure. The SPI bus +driver's documentation should be consulted for further details. + </para> + <para> +Both configuration functions will lock the bus, in the same way as +<function>cyg_spi_transfer</function>. Changing the clock rate in the +middle of a transfer or manipulating other parameters would have +unexpected consequences. + </para> + </refsect1> + +</refentry> + +<refentry id="spi-porting"> + <refmeta> + <refentrytitle>Porting to New Hardware</refentrytitle> + </refmeta> + <refnamediv> + <refname>Porting</refname> + <refpurpose>Adding SPI support to new hardware</refpurpose> + </refnamediv> + + <refsect1 id="spi-porting-description"><title>Description</title> + <para> +Adding SPI support to an eCos port can take two forms. If there is +already an SPI bus driver for the target hardware then both that +driver and this generic SPI package <varname>CYGPKG_IO_SPI</varname> +should be included in the <database>ecos.db</database> target entry. +Typically the platform HAL will need to supply some platform-specific +information needed by the bus driver. In addition the platform HAL +should provide <structname>cyg_spi_device</structname> structures for +every device attached to the bus. The exact details of this depend on +the bus driver so its documentation should be consulted for further +details. If there is no suitable SPI bus driver yet then a new driver +package will have to be written. + </para> + </refsect1> + + <refsect1 id="spi-porting-devices"><title>Adding a Device</title> + <para> +The generic SPI package <varname>CYGPKG_IO_SPI</varname> defines a +data structure <structname>cyg_spi_device</structname>. This contains +the information needed by the generic package, but not the additional +information needed by a bus driver to interact with the device. Each +bus driver will define a larger data structure, for example +<structname>cyg_mcf52xx_qspi_device</structname>, which contains a +<structname>cyg_spi_device</structname> as its first field. This is +analogous to C++ base and derived classes, but without any use of +virtual functions. The bus driver package should be consulted for the +details. + </para> + <para> +During initialization an SPI bus driver may need to know about all the +devices attached to that bus. For example it may need to know which +cpu pins should be configured as chip selects rather than GPIO pins. +To achieve this all device definitions should specify the particular +bus to which they are attached, for example: + </para> + <programlisting> +struct cyg_mcf52xx_qspi_device hal_spi_atod = { + .spi_common.spi_bus = &cyg_mcf52xx_qspi_bus, + … +} CYG_SPI_DEVICE_ON_BUS(0); + </programlisting> + <para> +The <function>CYG_SPI_DEVICE_ON_BUS</function> macro adds information +to the structure which causes the linker to group all such structures +in a single table. The bus driver's initialization code can then +iterate over this table. + </para> + </refsect1> + + <refsect1 id="spi-porting-bus"><title>Adding Bus Support</title> + <para> +An SPI bus driver usually involves a new hardware package. This needs +to perform the following: + </para> + <orderedlist> + <listitem><para> +Define a device structure which contains a +<structname>cyg_spi_device</structname> as its first element. This +should contain all the information needed by the bus driver to +interact with a device on that bus. + </para></listitem> + <listitem><para> +Provide functions for the following operations: + </para> + <simplelist type="vert" columns="1"> + <member><function>spi_transaction_begin</function></member> + <member><function>spi_transaction_transfer</function></member> + <member><function>spi_transaction_tick</function></member> + <member><function>spi_transaction_end</function></member> + <member><function>spi_get_config</function></member> + <member><function>spi_set_config</function></member> + </simplelist> + <para> +These correspond to the main API functions, but can assume that the +bus is already locked so no other thread will be manipulating the bus +or any of the attached devices. Some of these operations may be no-ops. + </para></listitem> + <listitem><para> +Define a bus structure which contains a +<structname>cyg_spi_bus</structname> as its first element. This should +contain any additional information needed by the bus driver. + </para></listitem> + <listitem><para> +Optionally, instantiate the bus structure. The instance should have a +well-known name since it needs to be referenced by the device +structure initializers. For some drivers it may be best to create the +bus inside the driver package. For other drivers it may be better to +leave this to the platform HAL or the application. It depends on how +much platform-specific knowledge is needed to fill in the bus +structure. + </para></listitem> + <listitem><para> +Create a HAL table for the devices attached to this bus. + </para></listitem> + <listitem><para> +Arrange for the bus to be initialized early on during system +initialization. Typically this will happen via a prioritized static +constructor. As part of this initialization the bus driver should +invoke the <function>CYG_SPI_BUS_COMMON_INIT</function> macro on its +<structname>cyg_spi_bus</structname> field. + </para></listitem> + <listitem><para> +Provide the appropriate documentation, including details of how the +SPI device structures should be initialized. + </para></listitem> + </orderedlist> + <para> +There are no standard SPI testcases. It is not possible to write SPI +code without knowing about the devices attached to the bus, and those +are inherently hardware-specific. + </para> + </refsect1> + +</refentry> +</part>
new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/doc/spi_net.fig @@ -0,0 +1,70 @@ +#FIG 3.2 +Landscape +Center +Inches +Letter +100.00 +Single +-2 +1200 2 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 4 + 1 1 1.00 60.00 120.00 + 2700 2400 3300 2400 3300 1200 5700 1200 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 4 + 1 1 1.00 60.00 120.00 + 2700 3600 3300 3600 3300 5100 5700 5100 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2 + 3900 1500 3900 4200 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 2700 2700 3900 2700 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3900 1500 5700 1500 +2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 3900 4200 5700 4200 +2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 + 4500 1800 4500 4500 +2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 + 5700 900 7200 900 7200 2400 5700 2400 5700 900 +2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 + 5700 3900 7200 3900 7200 5400 5700 5400 5700 3900 +2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 2700 3000 4500 3000 +2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 4500 1800 5700 1800 +2 1 1 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 4500 4500 5700 4500 +2 1 3 1 0 7 50 0 -1 4.000 0 0 -1 0 0 2 + 5100 2100 5100 4800 +2 1 3 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 5700 2100 5100 2100 +2 1 3 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 5625 4800 5100 4800 +2 1 3 1 0 7 50 0 -1 4.000 0 0 -1 1 0 2 + 1 1 1.00 60.00 120.00 + 5100 3300 2700 3300 +2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5 + 900 2100 2700 2100 2700 3900 900 3900 900 2100 +4 0 0 50 0 0 12 0.0000 4 135 315 2325 2400 CS0\001 +4 0 0 50 0 0 12 0.0000 4 135 360 2325 2775 CLK\001 +4 0 0 50 0 0 12 0.0000 4 135 225 5775 1275 CS\001 +4 0 0 50 0 0 12 0.0000 4 135 360 5775 1575 CLK\001 +4 0 0 50 0 0 12 0.0000 4 135 225 5775 5175 CS\001 +4 0 0 50 0 0 12 0.0000 4 135 360 5775 4275 CLK\001 +4 0 0 50 0 0 12 0.0000 4 135 315 2325 3675 CS1\001 +4 0 0 50 0 0 12 0.0000 4 135 465 2175 3075 MOSI\001 +4 0 0 50 0 0 12 0.0000 4 135 465 2175 3375 MISO\001 +4 0 0 50 0 0 12 0.0000 4 135 270 5775 2175 DO\001 +4 0 0 50 0 0 12 0.0000 4 135 195 5775 1875 DI\001 +4 0 0 50 0 0 12 0.0000 4 135 195 5775 4575 DI\001 +4 0 0 50 0 0 12 0.0000 4 135 270 5775 4875 DO\001 +4 0 0 50 0 0 20 0.0000 4 195 570 1050 3000 CPU\001 +4 0 0 50 0 0 20 0.0000 4 195 735 6450 1800 DEV1\001 +4 0 0 50 0 0 20 0.0000 4 195 735 6300 4800 DEV2\001
new file mode 100644 index 0000000000000000000000000000000000000000..7043dea17b4baa009f7d2b100947b80aa9fb96eb GIT binary patch literal 3372 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 EZ;C60Z2$lO
new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/include/spi.h @@ -0,0 +1,117 @@ +#ifndef CYGONCE_IO_SPI_H +#define CYGONCE_IO_SPI_H + +//============================================================================= +// +// spi.h +// +// Generic API for accessing devices on an SPI bus +// +//============================================================================= +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//============================================================================= +//####DESCRIPTIONBEGIN#### +// +// Author(s): bartv +// Date: 2004-04-23 +//####DESCRIPTIONEND#### +//============================================================================= + +#include <pkgconf/infra.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/hal/drv_api.h> +#include <cyg/hal/hal_tables.h> +#include <cyg/hal/hal_io.h> + +typedef struct cyg_spi_device { + struct cyg_spi_bus* spi_bus; +} cyg_spi_device; + +typedef struct cyg_spi_bus { + cyg_drv_mutex_t spi_lock; +#ifdef CYGDBG_USE_ASSERTS + cyg_spi_device* spi_current_device; +#endif + void (*spi_transaction_begin)(cyg_spi_device*); + void (*spi_transaction_transfer)(cyg_spi_device*, cyg_bool, cyg_uint32, const cyg_uint8*, cyg_uint8*, cyg_bool); + void (*spi_transaction_tick)(cyg_spi_device*, cyg_bool, cyg_uint32); + void (*spi_transaction_end)(cyg_spi_device*); + int (*spi_get_config)(cyg_spi_device*, cyg_uint32, void*, cyg_uint32*); + int (*spi_set_config)(cyg_spi_device*, cyg_uint32, const void*, cyg_uint32*); +} cyg_spi_bus; + +#ifdef CYGDBG_USE_ASSERTS +# define CYG_SPI_BUS_COMMON_INIT(_bus_) \ + CYG_MACRO_START \ + cyg_drv_mutex_init( & ((_bus_)->spi_lock)); \ + (_bus_)->spi_current_device = (cyg_spi_device*)0; \ + CYG_MACRO_END +#else +# define CYG_SPI_BUS_COMMON_INIT(_bus_) \ + CYG_MACRO_START \ + cyg_drv_mutex_init( & ((_bus_)->spi_lock)); \ + CYG_MACRO_END +#endif + +// All devices should be in a per-bus table +#define CYG_SPI_DEFINE_BUS_TABLE(_type_, _which_) \ + CYG_HAL_TABLE_BEGIN(cyg_spi_bus_ ## _which_ ## _devs, spibus_ ## _which_); \ + CYG_HAL_TABLE_END(cyg_spi_bus_ ## _which_ ## _devs_end, spibus_ ## _which_); \ + extern _type_ cyg_spi_bus_## _which_ ## _devs[], cyg_spi_bus_## _which_ ## _devs_end + +#define CYG_SPI_DEVICE_ON_BUS(_which_) CYG_HAL_TABLE_ENTRY( spibus_ ## _which_) + +// Keys for use with the get_config() and set_config() operations. +#define CYG_IO_GET_CONFIG_SPI_CLOCKRATE 0x00000800 +#define CYG_IO_SET_CONFIG_SPI_CLOCKRATE 0x00000880 + +// The simple I/O operations. +externC void cyg_spi_transfer(cyg_spi_device*, cyg_bool, cyg_uint32, const cyg_uint8*, cyg_uint8*); +externC void cyg_spi_tick(cyg_spi_device*, cyg_bool, cyg_uint32); +externC int cyg_spi_get_config(cyg_spi_device*, cyg_uint32, void*, cyg_uint32*); +externC int cyg_spi_set_config(cyg_spi_device*, cyg_uint32, const void*, cyg_uint32*); + +// Support for more complicated transactions. +externC void cyg_spi_transaction_begin(cyg_spi_device*); +externC cyg_bool cyg_spi_transaction_begin_nb(cyg_spi_device*); +externC void cyg_spi_transaction_transfer(cyg_spi_device*, cyg_bool, cyg_uint32, const cyg_uint8*, cyg_uint8*, cyg_bool); +externC void cyg_spi_transaction_tick(cyg_spi_device*, cyg_bool, cyg_uint32); +externC void cyg_spi_transaction_end(cyg_spi_device*); + +// Allow the HAL to export named devices, without introducing circular +// dependencies in the header files. +#ifdef HAL_SPI_EXPORTED_DEVICES + HAL_SPI_EXPORTED_DEVICES +#endif + +//----------------------------------------------------------------------------- +#endif // ifndef CYGONCE_IO_SPI_H +// End of spi.h
new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/src/spi.c @@ -0,0 +1,206 @@ +//========================================================================== +// +// spi.c +// +// Generic API for accessing devices attached to an SPI bus +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2004 eCosCentric Limited +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//###DESCRIPTIONBEGIN#### +// +// Author(s): bartv +// Date: 2004-04-23 +// +//###DESCRIPTIONEND#### +//======================================================================== + +#include <pkgconf/infra.h> +#include <pkgconf/io_spi.h> +#include <cyg/infra/cyg_ass.h> +#include <cyg/io/spi.h> + +// The simple transfer function just calls the transaction functions +// in order. The chip select should be dropped at the end of this +// transfer. +void +cyg_spi_transfer(cyg_spi_device* device, cyg_bool polled, cyg_uint32 count, const cyg_uint8* tx_data, cyg_uint8* rx_data) +{ + cyg_spi_transaction_begin(device); + cyg_spi_transaction_transfer(device, polled, count, tx_data, rx_data, 1); + cyg_spi_transaction_end(device); +} + +// Tick is similarly simple. +void +cyg_spi_tick(cyg_spi_device* device, cyg_bool polled, cyg_uint32 count) +{ + cyg_spi_transaction_begin(device); + cyg_spi_transaction_tick(device, polled, count); + cyg_spi_transaction_end(device); +} + +// get_config() and set_config() requires locking the bus, then accessing the +// bus-specific function via the bus structure. Only locking is needed, not +// setting up the bus for a transfer to this device, so the transaction begin +// function is inappropriate. +int +cyg_spi_get_config(cyg_spi_device* device, cyg_uint32 key, void* buf, cyg_uint32* len) +{ + int result; + cyg_spi_bus* bus; + CYG_CHECK_DATA_PTR(device, "valid SPI device pointer required"); + + bus = device->spi_bus; + CYG_CHECK_DATA_PTR(bus, "SPI device does not point at a valid bus structure"); + CYG_CHECK_FUNC_PTR(bus->spi_get_config, "SPI bus driver has not provided a get_config function"); + + while (!cyg_drv_mutex_lock(&(bus->spi_lock))); + result = (*(bus->spi_get_config))(device, key, buf, len); + cyg_drv_mutex_unlock(&(bus->spi_lock)); + + return result; +} + +// set_config is basically just a clone of the above, but using a different +// bus-specific operation. +int +cyg_spi_set_config(cyg_spi_device* device, cyg_uint32 key, const void* buf, cyg_uint32* len) +{ + int result; + cyg_spi_bus* bus; + CYG_CHECK_DATA_PTR(device, "valid SPI device pointer required"); + + bus = device->spi_bus; + CYG_CHECK_DATA_PTR(bus, "SPI device does not point at a valid bus structure"); + CYG_CHECK_FUNC_PTR(bus->spi_set_config, "SPI bus driver has not provided a set_config function"); + + while (!cyg_drv_mutex_lock(&(bus->spi_lock))); + result = (*(bus->spi_set_config))(device, key, buf, len); + cyg_drv_mutex_unlock(&(bus->spi_lock)); + + return result; +} + +// transaction-begin involves getting sole access to the bus, then calling +// the bus driver's begin function to set up the hardware appropriately for +// the target device. +void +cyg_spi_transaction_begin(cyg_spi_device* device) +{ + cyg_spi_bus* bus; + CYG_CHECK_DATA_PTR(device, "valid SPI device pointer required"); + + bus = device->spi_bus; + CYG_CHECK_DATA_PTR(bus, "SPI device does not point at a valid bus structure"); + CYG_CHECK_FUNC_PTR(bus->spi_transaction_begin, "SPI device has not provided a transaction_begin function"); + + while (!cyg_drv_mutex_lock(&(bus->spi_lock))); +#ifdef CYGDBG_USE_ASSERTS + bus->spi_current_device = device; +#endif + + // This thread now has exclusive access to the bus + (*(bus->spi_transaction_begin))(device); + + // All done. Return with the bus still locked. +} + +// A variant of the above which returns immediately if some other thread +// is using the bus. +cyg_bool +cyg_spi_transaction_begin_nb(cyg_spi_device* device) +{ + cyg_bool result = false; + cyg_spi_bus* bus; + CYG_CHECK_DATA_PTR(device, "valid SPI device pointer required"); + + bus = device->spi_bus; + CYG_CHECK_DATA_PTR(bus, "SPI device does not point at a valid bus structure"); + CYG_CHECK_FUNC_PTR(bus->spi_transaction_begin, "SPI device has not provided a transaction_begin function"); + + if (cyg_drv_mutex_trylock(&(bus->spi_lock))) { +#ifdef CYGDBG_USE_ASSERTS + bus->spi_current_device = device; +#endif + (*(bus->spi_transaction_begin))(device); + result = true; + } + + return result; +} + +void +cyg_spi_transaction_transfer(cyg_spi_device* device, cyg_bool polled, + cyg_uint32 count, const cyg_uint8* tx_data, cyg_uint8* rx_data, + cyg_bool drop_cs) +{ + cyg_spi_bus* bus; + CYG_CHECK_DATA_PTR(device, "valid SPI device pointer required"); + + bus = device->spi_bus; + CYG_CHECK_DATA_PTR(bus, "SPI device does not point at a valid bus structure"); + CYG_ASSERT(bus->spi_current_device == device, "SPI transfer requested without claiming the bus"); + CYG_CHECK_FUNC_PTR(bus->spi_transaction_transfer, "SPI device has not provided a transfer function"); + (*(bus->spi_transaction_transfer))(device, polled, count, tx_data, rx_data, drop_cs); +} + +void +cyg_spi_transaction_tick(cyg_spi_device* device, cyg_bool polled, cyg_uint32 count) +{ + cyg_spi_bus* bus; + CYG_CHECK_DATA_PTR(device, "valid SPI device pointer required"); + + bus = device->spi_bus; + CYG_CHECK_DATA_PTR(bus, "SPI device does not point at a valid bus structure"); + CYG_ASSERT(bus->spi_current_device == device, "SPI ticks requested without claiming the bus"); + CYG_CHECK_FUNC_PTR(bus->spi_transaction_tick, "SPI device has not provided a tick function"); + + (*(bus->spi_transaction_tick))(device, polled, count); +} + +void +cyg_spi_transaction_end(cyg_spi_device* device) +{ + cyg_spi_bus* bus; + CYG_CHECK_DATA_PTR(device, "valid SPI device pointer required"); + + bus = device->spi_bus; + CYG_CHECK_DATA_PTR(bus, "SPI device does not point at a valid bus structure"); + CYG_ASSERT(bus->spi_current_device == device, "SPI transfer requested without claiming the bus"); + CYG_CHECK_FUNC_PTR(bus->spi_transaction_end, "SPI device has not provided an end function"); + + // First, call the bus' end function. + (*(bus->spi_transaction_end))(device); + + // Then release the SPI bus for other threads. + cyg_drv_mutex_unlock(&(bus->spi_lock)); +}
