mirror of
https://github.com/torvalds/linux.git
synced 2024-11-24 21:21:41 +00:00
spi: imx: support word delay in ecspi
Merge series from Jonas Rebmann <jre@pengutronix.de>: The i.MX SPI controller supports inserting a configurable delay between subsequent words, which is needed for some slower devices that couldn't keep up otherwise. This patch series introduces support for the word delay parameters for i.MX51 onwards. The SPI clock (CSRC=0) was chosen as the clock source over the also available 32.768 KHz Low-Frequency Reference Clock (CSRC=1). The sample period control bits (SAMPLE_PERIOD) are set to the selected word delay converted to SPI clock cycles. A deviation from the requested number of wait cycles and the actual word delay was observed via both software timings and oscilloscope measurements and accounted for. The Chip Select Delay Control bits in the Sample Period Control Register remain zero. Behaviour on i.MX35 and earlier, where the CSPI interface is used, remains unchanged.
This commit is contained in:
commit
26470a2e87
@ -3,6 +3,7 @@
|
|||||||
// Copyright (C) 2008 Juergen Beisert
|
// Copyright (C) 2008 Juergen Beisert
|
||||||
|
|
||||||
#include <linux/bits.h>
|
#include <linux/bits.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
@ -13,7 +14,10 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/math.h>
|
||||||
|
#include <linux/math64.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
#include <linux/pinctrl/consumer.h>
|
#include <linux/pinctrl/consumer.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
@ -71,7 +75,8 @@ struct spi_imx_data;
|
|||||||
struct spi_imx_devtype_data {
|
struct spi_imx_devtype_data {
|
||||||
void (*intctrl)(struct spi_imx_data *spi_imx, int enable);
|
void (*intctrl)(struct spi_imx_data *spi_imx, int enable);
|
||||||
int (*prepare_message)(struct spi_imx_data *spi_imx, struct spi_message *msg);
|
int (*prepare_message)(struct spi_imx_data *spi_imx, struct spi_message *msg);
|
||||||
int (*prepare_transfer)(struct spi_imx_data *spi_imx, struct spi_device *spi);
|
int (*prepare_transfer)(struct spi_imx_data *spi_imx, struct spi_device *spi,
|
||||||
|
struct spi_transfer *t);
|
||||||
void (*trigger)(struct spi_imx_data *spi_imx);
|
void (*trigger)(struct spi_imx_data *spi_imx);
|
||||||
int (*rx_available)(struct spi_imx_data *spi_imx);
|
int (*rx_available)(struct spi_imx_data *spi_imx);
|
||||||
void (*reset)(struct spi_imx_data *spi_imx);
|
void (*reset)(struct spi_imx_data *spi_imx);
|
||||||
@ -301,6 +306,18 @@ static bool spi_imx_can_dma(struct spi_controller *controller, struct spi_device
|
|||||||
#define MX51_ECSPI_STAT 0x18
|
#define MX51_ECSPI_STAT 0x18
|
||||||
#define MX51_ECSPI_STAT_RR (1 << 3)
|
#define MX51_ECSPI_STAT_RR (1 << 3)
|
||||||
|
|
||||||
|
#define MX51_ECSPI_PERIOD 0x1c
|
||||||
|
#define MX51_ECSPI_PERIOD_MASK 0x7fff
|
||||||
|
/*
|
||||||
|
* As measured on the i.MX6, the SPI host controller inserts a 4 SPI-Clock
|
||||||
|
* (SCLK) delay after each burst if the PERIOD reg is 0x0. This value will be
|
||||||
|
* called MX51_ECSPI_PERIOD_MIN_DELAY_SCK.
|
||||||
|
*
|
||||||
|
* If the PERIOD register is != 0, the controller inserts a delay of
|
||||||
|
* MX51_ECSPI_PERIOD_MIN_DELAY_SCK + register value + 1 SCLK after each burst.
|
||||||
|
*/
|
||||||
|
#define MX51_ECSPI_PERIOD_MIN_DELAY_SCK 4
|
||||||
|
|
||||||
#define MX51_ECSPI_TESTREG 0x20
|
#define MX51_ECSPI_TESTREG 0x20
|
||||||
#define MX51_ECSPI_TESTREG_LBC BIT(31)
|
#define MX51_ECSPI_TESTREG_LBC BIT(31)
|
||||||
|
|
||||||
@ -648,9 +665,10 @@ static void mx51_configure_cpha(struct spi_imx_data *spi_imx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
|
static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||||
struct spi_device *spi)
|
struct spi_device *spi, struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
|
||||||
|
u64 word_delay_sck;
|
||||||
u32 clk;
|
u32 clk;
|
||||||
|
|
||||||
/* Clear BL field and set the right value */
|
/* Clear BL field and set the right value */
|
||||||
@ -682,6 +700,49 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
|
|||||||
|
|
||||||
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
|
writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
|
||||||
|
|
||||||
|
/* calculate word delay in SPI Clock (SCLK) cycles */
|
||||||
|
if (t->word_delay.value == 0) {
|
||||||
|
word_delay_sck = 0;
|
||||||
|
} else if (t->word_delay.unit == SPI_DELAY_UNIT_SCK) {
|
||||||
|
word_delay_sck = t->word_delay.value;
|
||||||
|
|
||||||
|
if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK)
|
||||||
|
word_delay_sck = 0;
|
||||||
|
else if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1)
|
||||||
|
word_delay_sck = 1;
|
||||||
|
else
|
||||||
|
word_delay_sck -= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1;
|
||||||
|
} else {
|
||||||
|
int word_delay_ns;
|
||||||
|
|
||||||
|
word_delay_ns = spi_delay_to_ns(&t->word_delay, t);
|
||||||
|
if (word_delay_ns < 0)
|
||||||
|
return word_delay_ns;
|
||||||
|
|
||||||
|
if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
|
||||||
|
MX51_ECSPI_PERIOD_MIN_DELAY_SCK,
|
||||||
|
spi_imx->spi_bus_clk)) {
|
||||||
|
word_delay_sck = 0;
|
||||||
|
} else if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
|
||||||
|
MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
|
||||||
|
spi_imx->spi_bus_clk)) {
|
||||||
|
word_delay_sck = 1;
|
||||||
|
} else {
|
||||||
|
word_delay_ns -= mul_u64_u32_div(NSEC_PER_SEC,
|
||||||
|
MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
|
||||||
|
spi_imx->spi_bus_clk);
|
||||||
|
|
||||||
|
word_delay_sck = DIV_U64_ROUND_UP((u64)word_delay_ns * spi_imx->spi_bus_clk,
|
||||||
|
NSEC_PER_SEC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FIELD_FIT(MX51_ECSPI_PERIOD_MASK, word_delay_sck))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
writel(FIELD_PREP(MX51_ECSPI_PERIOD_MASK, word_delay_sck),
|
||||||
|
spi_imx->base + MX51_ECSPI_PERIOD);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,7 +834,7 @@ static int mx31_prepare_message(struct spi_imx_data *spi_imx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int mx31_prepare_transfer(struct spi_imx_data *spi_imx,
|
static int mx31_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||||
struct spi_device *spi)
|
struct spi_device *spi, struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_HOST;
|
unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_HOST;
|
||||||
unsigned int clk;
|
unsigned int clk;
|
||||||
@ -877,7 +938,7 @@ static int mx21_prepare_message(struct spi_imx_data *spi_imx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int mx21_prepare_transfer(struct spi_imx_data *spi_imx,
|
static int mx21_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||||
struct spi_device *spi)
|
struct spi_device *spi, struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_HOST;
|
unsigned int reg = MX21_CSPICTRL_ENABLE | MX21_CSPICTRL_HOST;
|
||||||
unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18;
|
unsigned int max = is_imx27_cspi(spi_imx) ? 16 : 18;
|
||||||
@ -952,7 +1013,7 @@ static int mx1_prepare_message(struct spi_imx_data *spi_imx,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int mx1_prepare_transfer(struct spi_imx_data *spi_imx,
|
static int mx1_prepare_transfer(struct spi_imx_data *spi_imx,
|
||||||
struct spi_device *spi)
|
struct spi_device *spi, struct spi_transfer *t)
|
||||||
{
|
{
|
||||||
unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_HOST;
|
unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_HOST;
|
||||||
unsigned int clk;
|
unsigned int clk;
|
||||||
@ -1262,11 +1323,13 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the functions for transfer. To transfer non byte-aligned
|
* Initialize the functions for transfer. To transfer non byte-aligned
|
||||||
* words, we have to use multiple word-size bursts, we can't use
|
* words, we have to use multiple word-size bursts. To insert word
|
||||||
* dynamic_burst in that case.
|
* delay, the burst size has to equal the word size. We can't use
|
||||||
|
* dynamic_burst in these cases.
|
||||||
*/
|
*/
|
||||||
if (spi_imx->devtype_data->dynamic_burst && !spi_imx->target_mode &&
|
if (spi_imx->devtype_data->dynamic_burst && !spi_imx->target_mode &&
|
||||||
!(spi->mode & SPI_CS_WORD) &&
|
!(spi->mode & SPI_CS_WORD) &&
|
||||||
|
!(t->word_delay.value) &&
|
||||||
(spi_imx->bits_per_word == 8 ||
|
(spi_imx->bits_per_word == 8 ||
|
||||||
spi_imx->bits_per_word == 16 ||
|
spi_imx->bits_per_word == 16 ||
|
||||||
spi_imx->bits_per_word == 32)) {
|
spi_imx->bits_per_word == 32)) {
|
||||||
@ -1303,7 +1366,7 @@ static int spi_imx_setupxfer(struct spi_device *spi,
|
|||||||
spi_imx->target_burst = t->len;
|
spi_imx->target_burst = t->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
spi_imx->devtype_data->prepare_transfer(spi_imx, spi);
|
spi_imx->devtype_data->prepare_transfer(spi_imx, spi, t);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1609,12 +1672,30 @@ static int spi_imx_pio_transfer_target(struct spi_device *spi,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int spi_imx_transfer_estimate_time_us(struct spi_transfer *transfer)
|
||||||
|
{
|
||||||
|
u64 result;
|
||||||
|
|
||||||
|
result = DIV_U64_ROUND_CLOSEST((u64)USEC_PER_SEC * transfer->len * BITS_PER_BYTE,
|
||||||
|
transfer->effective_speed_hz);
|
||||||
|
if (transfer->word_delay.value) {
|
||||||
|
unsigned int word_delay_us;
|
||||||
|
unsigned int words;
|
||||||
|
|
||||||
|
words = DIV_ROUND_UP(transfer->len * BITS_PER_BYTE, transfer->bits_per_word);
|
||||||
|
word_delay_us = DIV_ROUND_CLOSEST(spi_delay_to_ns(&transfer->word_delay, transfer),
|
||||||
|
NSEC_PER_USEC);
|
||||||
|
result += words * word_delay_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
return min(result, U32_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
static int spi_imx_transfer_one(struct spi_controller *controller,
|
static int spi_imx_transfer_one(struct spi_controller *controller,
|
||||||
struct spi_device *spi,
|
struct spi_device *spi,
|
||||||
struct spi_transfer *transfer)
|
struct spi_transfer *transfer)
|
||||||
{
|
{
|
||||||
struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
|
struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
|
||||||
unsigned long hz_per_byte, byte_limit;
|
|
||||||
|
|
||||||
spi_imx_setupxfer(spi, transfer);
|
spi_imx_setupxfer(spi, transfer);
|
||||||
transfer->effective_speed_hz = spi_imx->spi_bus_clk;
|
transfer->effective_speed_hz = spi_imx->spi_bus_clk;
|
||||||
@ -1633,15 +1714,10 @@ static int spi_imx_transfer_one(struct spi_controller *controller,
|
|||||||
*/
|
*/
|
||||||
if (spi_imx->usedma)
|
if (spi_imx->usedma)
|
||||||
return spi_imx_dma_transfer(spi_imx, transfer);
|
return spi_imx_dma_transfer(spi_imx, transfer);
|
||||||
/*
|
|
||||||
* Calculate the estimated time in us the transfer runs. Find
|
|
||||||
* the number of Hz per byte per polling limit.
|
|
||||||
*/
|
|
||||||
hz_per_byte = polling_limit_us ? ((8 + 4) * USEC_PER_SEC) / polling_limit_us : 0;
|
|
||||||
byte_limit = hz_per_byte ? transfer->effective_speed_hz / hz_per_byte : 1;
|
|
||||||
|
|
||||||
/* run in polling mode for short transfers */
|
/* run in polling mode for short transfers */
|
||||||
if (transfer->len < byte_limit)
|
if (transfer->len == 1 || (polling_limit_us &&
|
||||||
|
spi_imx_transfer_estimate_time_us(transfer) < polling_limit_us))
|
||||||
return spi_imx_poll_transfer(spi, transfer);
|
return spi_imx_poll_transfer(spi, transfer);
|
||||||
|
|
||||||
return spi_imx_pio_transfer(spi, transfer);
|
return spi_imx_pio_transfer(spi, transfer);
|
||||||
|
Loading…
Reference in New Issue
Block a user