209 lines
6.9 KiB
Plaintext
209 lines
6.9 KiB
Plaintext
|
- CAIF SPI porting -
|
||
|
|
||
|
- CAIF SPI basics:
|
||
|
|
||
|
Running CAIF over SPI needs some extra setup, owing to the nature of SPI.
|
||
|
Two extra GPIOs have been added in order to negotiate the transfers
|
||
|
between the master and the slave. The minimum requirement for running
|
||
|
CAIF over SPI is a SPI slave chip and two GPIOs (more details below).
|
||
|
Please note that running as a slave implies that you need to keep up
|
||
|
with the master clock. An overrun or underrun event is fatal.
|
||
|
|
||
|
- CAIF SPI framework:
|
||
|
|
||
|
To make porting as easy as possible, the CAIF SPI has been divided in
|
||
|
two parts. The first part (called the interface part) deals with all
|
||
|
generic functionality such as length framing, SPI frame negotiation
|
||
|
and SPI frame delivery and transmission. The other part is the CAIF
|
||
|
SPI slave device part, which is the module that you have to write if
|
||
|
you want to run SPI CAIF on a new hardware. This part takes care of
|
||
|
the physical hardware, both with regard to SPI and to GPIOs.
|
||
|
|
||
|
- Implementing a CAIF SPI device:
|
||
|
|
||
|
- Functionality provided by the CAIF SPI slave device:
|
||
|
|
||
|
In order to implement a SPI device you will, as a minimum,
|
||
|
need to implement the following
|
||
|
functions:
|
||
|
|
||
|
int (*init_xfer) (struct cfspi_xfer * xfer, struct cfspi_dev *dev):
|
||
|
|
||
|
This function is called by the CAIF SPI interface to give
|
||
|
you a chance to set up your hardware to be ready to receive
|
||
|
a stream of data from the master. The xfer structure contains
|
||
|
both physical and logical addresses, as well as the total length
|
||
|
of the transfer in both directions.The dev parameter can be used
|
||
|
to map to different CAIF SPI slave devices.
|
||
|
|
||
|
void (*sig_xfer) (bool xfer, struct cfspi_dev *dev):
|
||
|
|
||
|
This function is called by the CAIF SPI interface when the output
|
||
|
(SPI_INT) GPIO needs to change state. The boolean value of the xfer
|
||
|
variable indicates whether the GPIO should be asserted (HIGH) or
|
||
|
deasserted (LOW). The dev parameter can be used to map to different CAIF
|
||
|
SPI slave devices.
|
||
|
|
||
|
- Functionality provided by the CAIF SPI interface:
|
||
|
|
||
|
void (*ss_cb) (bool assert, struct cfspi_ifc *ifc);
|
||
|
|
||
|
This function is called by the CAIF SPI slave device in order to
|
||
|
signal a change of state of the input GPIO (SS) to the interface.
|
||
|
Only active edges are mandatory to be reported.
|
||
|
This function can be called from IRQ context (recommended in order
|
||
|
not to introduce latency). The ifc parameter should be the pointer
|
||
|
returned from the platform probe function in the SPI device structure.
|
||
|
|
||
|
void (*xfer_done_cb) (struct cfspi_ifc *ifc);
|
||
|
|
||
|
This function is called by the CAIF SPI slave device in order to
|
||
|
report that a transfer is completed. This function should only be
|
||
|
called once both the transmission and the reception are completed.
|
||
|
This function can be called from IRQ context (recommended in order
|
||
|
not to introduce latency). The ifc parameter should be the pointer
|
||
|
returned from the platform probe function in the SPI device structure.
|
||
|
|
||
|
- Connecting the bits and pieces:
|
||
|
|
||
|
- Filling in the SPI slave device structure:
|
||
|
|
||
|
Connect the necessary callback functions.
|
||
|
Indicate clock speed (used to calculate toggle delays).
|
||
|
Chose a suitable name (helps debugging if you use several CAIF
|
||
|
SPI slave devices).
|
||
|
Assign your private data (can be used to map to your structure).
|
||
|
|
||
|
- Filling in the SPI slave platform device structure:
|
||
|
Add name of driver to connect to ("cfspi_sspi").
|
||
|
Assign the SPI slave device structure as platform data.
|
||
|
|
||
|
- Padding:
|
||
|
|
||
|
In order to optimize throughput, a number of SPI padding options are provided.
|
||
|
Padding can be enabled independently for uplink and downlink transfers.
|
||
|
Padding can be enabled for the head, the tail and for the total frame size.
|
||
|
The padding needs to be correctly configured on both sides of the link.
|
||
|
The padding can be changed via module parameters in cfspi_sspi.c or via
|
||
|
the sysfs directory of the cfspi_sspi driver (before device registration).
|
||
|
|
||
|
- CAIF SPI device template:
|
||
|
|
||
|
/*
|
||
|
* Copyright (C) ST-Ericsson AB 2010
|
||
|
* Author: Daniel Martensson / Daniel.Martensson@stericsson.com
|
||
|
* License terms: GNU General Public License (GPL), version 2.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <linux/init.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/device.h>
|
||
|
#include <linux/wait.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/dma-mapping.h>
|
||
|
#include <net/caif/caif_spi.h>
|
||
|
|
||
|
MODULE_LICENSE("GPL");
|
||
|
|
||
|
struct sspi_struct {
|
||
|
struct cfspi_dev sdev;
|
||
|
struct cfspi_xfer *xfer;
|
||
|
};
|
||
|
|
||
|
static struct sspi_struct slave;
|
||
|
static struct platform_device slave_device;
|
||
|
|
||
|
static irqreturn_t sspi_irq(int irq, void *arg)
|
||
|
{
|
||
|
/* You only need to trigger on an edge to the active state of the
|
||
|
* SS signal. Once a edge is detected, the ss_cb() function should be
|
||
|
* called with the parameter assert set to true. It is OK
|
||
|
* (and even advised) to call the ss_cb() function in IRQ context in
|
||
|
* order not to add any delay. */
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static void sspi_complete(void *context)
|
||
|
{
|
||
|
/* Normally the DMA or the SPI framework will call you back
|
||
|
* in something similar to this. The only thing you need to
|
||
|
* do is to call the xfer_done_cb() function, providing the pointer
|
||
|
* to the CAIF SPI interface. It is OK to call this function
|
||
|
* from IRQ context. */
|
||
|
}
|
||
|
|
||
|
static int sspi_init_xfer(struct cfspi_xfer *xfer, struct cfspi_dev *dev)
|
||
|
{
|
||
|
/* Store transfer info. For a normal implementation you should
|
||
|
* set up your DMA here and make sure that you are ready to
|
||
|
* receive the data from the master SPI. */
|
||
|
|
||
|
struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
|
||
|
|
||
|
sspi->xfer = xfer;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void sspi_sig_xfer(bool xfer, struct cfspi_dev *dev)
|
||
|
{
|
||
|
/* If xfer is true then you should assert the SPI_INT to indicate to
|
||
|
* the master that you are ready to receive the data from the master
|
||
|
* SPI. If xfer is false then you should de-assert SPI_INT to indicate
|
||
|
* that the transfer is done.
|
||
|
*/
|
||
|
|
||
|
struct sspi_struct *sspi = (struct sspi_struct *)dev->priv;
|
||
|
}
|
||
|
|
||
|
static void sspi_release(struct device *dev)
|
||
|
{
|
||
|
/*
|
||
|
* Here you should release your SPI device resources.
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
static int __init sspi_init(void)
|
||
|
{
|
||
|
/* Here you should initialize your SPI device by providing the
|
||
|
* necessary functions, clock speed, name and private data. Once
|
||
|
* done, you can register your device with the
|
||
|
* platform_device_register() function. This function will return
|
||
|
* with the CAIF SPI interface initialized. This is probably also
|
||
|
* the place where you should set up your GPIOs, interrupts and SPI
|
||
|
* resources. */
|
||
|
|
||
|
int res = 0;
|
||
|
|
||
|
/* Initialize slave device. */
|
||
|
slave.sdev.init_xfer = sspi_init_xfer;
|
||
|
slave.sdev.sig_xfer = sspi_sig_xfer;
|
||
|
slave.sdev.clk_mhz = 13;
|
||
|
slave.sdev.priv = &slave;
|
||
|
slave.sdev.name = "spi_sspi";
|
||
|
slave_device.dev.release = sspi_release;
|
||
|
|
||
|
/* Initialize platform device. */
|
||
|
slave_device.name = "cfspi_sspi";
|
||
|
slave_device.dev.platform_data = &slave.sdev;
|
||
|
|
||
|
/* Register platform device. */
|
||
|
res = platform_device_register(&slave_device);
|
||
|
if (res) {
|
||
|
printk(KERN_WARNING "sspi_init: failed to register dev.\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
static void __exit sspi_exit(void)
|
||
|
{
|
||
|
platform_device_del(&slave_device);
|
||
|
}
|
||
|
|
||
|
module_init(sspi_init);
|
||
|
module_exit(sspi_exit);
|