# HG changeset patch # User bartv # Date 1093368676 0 # Node ID 77d1baeea166b374b45d195899f08fcc0b4a5aee # Parent 171cd3a5df5db1c1476b4705dfcd6da871edb42e Add generic SPI support diff --git a/packages/ChangeLog b/packages/ChangeLog --- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,7 @@ +2004-08-24 Bart Veer + + * ecos.db: add SPI package + 2004-08-06 Jani Monoses * ecos.db: Remove two obsolete flash drivers: INTEL_BOOTBLOCK diff --git a/packages/ecos.db b/packages/ecos.db --- 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 diff --git a/packages/io/spi/current/ChangeLog b/packages/io/spi/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/ChangeLog @@ -0,0 +1,35 @@ +2004-04-23 Bart Veer + + * 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#### +//=========================================================================== diff --git a/packages/io/spi/current/cdl/spi.cdl b/packages/io/spi/current/cdl/spi.cdl 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." + } + } +} diff --git a/packages/io/spi/current/doc/spi.sgml b/packages/io/spi/current/doc/spi.sgml new file mode 100644 --- /dev/null +++ b/packages/io/spi/current/doc/spi.sgml @@ -0,0 +1,711 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +SPI Support + + + + Overview + + + Overview + eCos Support for SPI, the Serial Peripheral Interface + + + Description + +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. + + +A typical SPI system might look like this: + + + + + + + + + +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. + + +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. + + +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. + + +SPI communication is not fully standardized. Variations between +devices include the following: + + + +Many devices involve byte transfers, where the unit of data is 8 bits. +Others use larger units, up to 16 bits. + + +Chip selects may be active-high or active-low. If the attached devices +use a mixture of polarities then this can complicate things. + + +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. + + +The clock is inactive between data transfers. When inactive the +clock's polarity can be high or low. + + +Devices depend on the phase of the clock. Data may be sampled on +either the rising edge or the falling edge of the clock. + + +A device may need additional delays, for example between asserting +the chip select and the first clock tick. + + +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. + + +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. + + + +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. + + + + eCos Support for SPI + +The eCos SPI support for any given platform is spread over a number of +different packages: + + + +This package, CYGPKG_IO_SPI, 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. + + +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. + + +There will be a bus driver package for the specific SPI hardware on +the target hardware, for example +CYGPKG_DEVS_SPI_MCF52xx_QSPI. 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. + + +The generic API depends on cyg_spi_device +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. + + +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. + + +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. + + + +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. + + +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. + + + + + + + + SPI Interface + + + SPI Functions + allow applications and other packages to access SPI devices + + + + +#include <cyg/io/spi.h> + + + void cyg_spi_transfer + cyg_spi_device* device + cyg_bool polled + cyg_uint32 count + const cyg_uint8* tx_data + cyg_uint8* rx_data + + + void cyg_spi_tick + cyg_spi_device* device + cyg_bool polled + cyg_uint32 count + + + int cyg_spi_get_config + cyg_spi_device* device + cyg_uint32 key + void* buf + cyg_uint32* len + + + int cyg_spi_set_config + cyg_spi_device* device + cyg_uint32 key + const void* buf + cyg_uint32* len + + + void cyg_spi_transaction_begin + cyg_spi_device* device + + + cyg_bool cyg_spi_transaction_begin_nb + cyg_spi_device* device + + + 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 + + + void cyg_spi_transaction_tick + cyg_spi_device* device + cyg_bool polled + cyg_uint32 count + + + void cyg_spi_transaction_end + cyg_spi_device* device + + + + + Description + +All SPI functions take a pointer to a +cyg_spi_device 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. + + +An SPI transfer involves the following stages: + + + +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. + + +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. + + +Optionally generate some number of clock ticks without asserting a +chip select, for those devices which need this to complete an +operation. + + +Return the bus to a quiescent state. Then unlock the bus, allowing +other threads to perform SPI operations on devices attached to this +bus. + + + +The simple functions cyg_spi_transfer and +cyg_spi_tick 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. + + +With the exception of +cyg_spi_transaction_begin_nb 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. + + +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. + + +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 polled argument. + + + + Simple Transfers + +cyg_spi_transfer can be used for SPI operations +to simple devices. It takes the following arguments: + + + + cyg_spi_device* device + +This identifies the SPI device that should be used. + + + + cyg_bool polled + +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. + + + + cyg_uint32 count + +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. + + + + const cyg_uint8* tx_data + +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 count 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. + + + + cyg_uint8* rx_data + +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 tx_data. + + + + +cyg_spi_transfer 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. + + + + Additional Clock Ticks + +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. +cyg_spi_tick provides this functionality. +The device argument identifies the SPI bus, the +required clock rate and the size of each data item. The +polled argument has the usual meaning. The +count 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. + + + + Transactions + +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. + + +cyg_spi_transaction_begin 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. + + +cyg_spi_transaction_begin_nb 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 +cyg_spi_transaction_begin and returns true. + + +Once the bus has been locked it is possible to perform one or more +data transfers by calling +cyg_spi_transaction_transfer. This takes the same +arguments as cyg_spi_transfer, plus an additional +one drop_cs. 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 +cyg_spi_transaction_tick can be used to generate +dummy ticks with all chip selects dropped. + + +If the device requires additional clock ticks in the middle of a +transaction without being selected, +cyg_spi_transaction_tick 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 +cyg_spi_tick. + + +cyg_spi_transaction_end 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. + + +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: + + +#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); + + +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. + + + + Device Configuration + +The functions cyg_spi_get_config and +cyg_spi_set_config can be used to examine and +change parameters associated with SPI transfers. The only keys that +are defined for all devices are +CYG_IO_GET_CONFIG_SPI_CLOCKRATE and +CYG_IO_SET_CONFIG_SPI_CLOCKRATE. Some types of +device, for example MMC cards, support a range of clock rates. The +cyg_spi_device structure will be initialized +with a low clock rate. During system initialization the device will be +queried for the optimal clock rate, and the +cyg_spi_device should then be updated. The +argument should be a clock rate in Hertz. For example the following +code switches communication to 1Mbit/s: + + + 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 + } + + +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 +cyg_spi_get_config and +cyg_spi_set_config 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. + + +Both configuration functions will lock the bus, in the same way as +cyg_spi_transfer. Changing the clock rate in the +middle of a transfer or manipulating other parameters would have +unexpected consequences. + + + + + + + + Porting to New Hardware + + + Porting + Adding SPI support to new hardware + + + Description + +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 CYGPKG_IO_SPI +should be included in the ecos.db 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 cyg_spi_device 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. + + + + Adding a Device + +The generic SPI package CYGPKG_IO_SPI defines a +data structure cyg_spi_device. 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 +cyg_mcf52xx_qspi_device, which contains a +cyg_spi_device 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. + + +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: + + +struct cyg_mcf52xx_qspi_device hal_spi_atod = { + .spi_common.spi_bus = &cyg_mcf52xx_qspi_bus, + … +} CYG_SPI_DEVICE_ON_BUS(0); + + +The CYG_SPI_DEVICE_ON_BUS 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. + + + + Adding Bus Support + +An SPI bus driver usually involves a new hardware package. This needs +to perform the following: + + + +Define a device structure which contains a +cyg_spi_device as its first element. This +should contain all the information needed by the bus driver to +interact with a device on that bus. + + +Provide functions for the following operations: + + + spi_transaction_begin + spi_transaction_transfer + spi_transaction_tick + spi_transaction_end + spi_get_config + spi_set_config + + +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. + + +Define a bus structure which contains a +cyg_spi_bus as its first element. This should +contain any additional information needed by the bus driver. + + +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. + + +Create a HAL table for the devices attached to this bus. + + +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 CYG_SPI_BUS_COMMON_INIT macro on its +cyg_spi_bus field. + + +Provide the appropriate documentation, including details of how the +SPI device structures should be initialized. + + + +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. + + + + + diff --git a/packages/io/spi/current/doc/spi_net.fig b/packages/io/spi/current/doc/spi_net.fig 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 diff --git a/packages/io/spi/current/doc/spi_net.png b/packages/io/spi/current/doc/spi_net.png new file mode 100644 index 0000000000000000000000000000000000000000..7043dea17b4baa009f7d2b100947b80aa9fb96eb GIT binary patch literal 3372 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 +#include +#include +#include +#include + +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 diff --git a/packages/io/spi/current/src/spi.c b/packages/io/spi/current/src/spi.c 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 +#include +#include +#include + +// 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)); +}