Merge branch 'spi-5.5' into spi-next
This commit is contained in:
@ -92,7 +92,7 @@ static ssize_t driver_override_store(struct device *dev,
|
||||
if (len) {
|
||||
spi->driver_override = driver_override;
|
||||
} else {
|
||||
/* Emptry string, disable driver override */
|
||||
/* Empty string, disable driver override */
|
||||
spi->driver_override = NULL;
|
||||
kfree(driver_override);
|
||||
}
|
||||
@ -469,7 +469,7 @@ static LIST_HEAD(board_list);
|
||||
static LIST_HEAD(spi_controller_list);
|
||||
|
||||
/*
|
||||
* Used to protect add/del opertion for board_info list and
|
||||
* Used to protect add/del operation for board_info list and
|
||||
* spi_controller list, and their matching process
|
||||
* also used to protect object of type struct idr
|
||||
*/
|
||||
@ -775,6 +775,15 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
|
||||
|
||||
static void spi_set_cs(struct spi_device *spi, bool enable)
|
||||
{
|
||||
bool enable1 = enable;
|
||||
|
||||
if (!spi->controller->set_cs_timing) {
|
||||
if (enable1)
|
||||
spi_delay_exec(&spi->controller->cs_setup, NULL);
|
||||
else
|
||||
spi_delay_exec(&spi->controller->cs_hold, NULL);
|
||||
}
|
||||
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
enable = !enable;
|
||||
|
||||
@ -800,6 +809,11 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
|
||||
} else if (spi->controller->set_cs) {
|
||||
spi->controller->set_cs(spi, !enable);
|
||||
}
|
||||
|
||||
if (!spi->controller->set_cs_timing) {
|
||||
if (!enable1)
|
||||
spi_delay_exec(&spi->controller->cs_inactive, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HAS_DMA
|
||||
@ -1106,42 +1120,79 @@ static void _spi_transfer_delay_ns(u32 ns)
|
||||
}
|
||||
}
|
||||
|
||||
static void _spi_transfer_cs_change_delay(struct spi_message *msg,
|
||||
struct spi_transfer *xfer)
|
||||
int spi_delay_to_ns(struct spi_delay *_delay, struct spi_transfer *xfer)
|
||||
{
|
||||
u32 delay = xfer->cs_change_delay;
|
||||
u32 unit = xfer->cs_change_delay_unit;
|
||||
u32 delay = _delay->value;
|
||||
u32 unit = _delay->unit;
|
||||
u32 hz;
|
||||
|
||||
/* return early on "fast" mode - for everything but USECS */
|
||||
if (!delay && unit != SPI_DELAY_UNIT_USECS)
|
||||
return;
|
||||
if (!delay)
|
||||
return 0;
|
||||
|
||||
switch (unit) {
|
||||
case SPI_DELAY_UNIT_USECS:
|
||||
/* for compatibility use default of 10us */
|
||||
if (!delay)
|
||||
delay = 10000;
|
||||
else
|
||||
delay *= 1000;
|
||||
delay *= 1000;
|
||||
break;
|
||||
case SPI_DELAY_UNIT_NSECS: /* nothing to do here */
|
||||
break;
|
||||
case SPI_DELAY_UNIT_SCK:
|
||||
/* clock cycles need to be obtained from spi_transfer */
|
||||
if (!xfer)
|
||||
return -EINVAL;
|
||||
/* if there is no effective speed know, then approximate
|
||||
* by underestimating with half the requested hz
|
||||
*/
|
||||
hz = xfer->effective_speed_hz ?: xfer->speed_hz / 2;
|
||||
if (!hz)
|
||||
return -EINVAL;
|
||||
delay *= DIV_ROUND_UP(1000000000, hz);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return delay;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_delay_to_ns);
|
||||
|
||||
int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer)
|
||||
{
|
||||
int delay;
|
||||
|
||||
if (!_delay)
|
||||
return -EINVAL;
|
||||
|
||||
delay = spi_delay_to_ns(_delay, xfer);
|
||||
if (delay < 0)
|
||||
return delay;
|
||||
|
||||
_spi_transfer_delay_ns(delay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_delay_exec);
|
||||
|
||||
static void _spi_transfer_cs_change_delay(struct spi_message *msg,
|
||||
struct spi_transfer *xfer)
|
||||
{
|
||||
u32 delay = xfer->cs_change_delay.value;
|
||||
u32 unit = xfer->cs_change_delay.unit;
|
||||
int ret;
|
||||
|
||||
/* return early on "fast" mode - for everything but USECS */
|
||||
if (!delay) {
|
||||
if (unit == SPI_DELAY_UNIT_USECS)
|
||||
_spi_transfer_delay_ns(10000);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = spi_delay_exec(&xfer->cs_change_delay, xfer);
|
||||
if (ret) {
|
||||
dev_err_once(&msg->spi->dev,
|
||||
"Use of unsupported delay unit %i, using default of 10us\n",
|
||||
xfer->cs_change_delay_unit);
|
||||
delay = 10000;
|
||||
unit);
|
||||
_spi_transfer_delay_ns(10000);
|
||||
}
|
||||
/* now sleep for the requested amount of time */
|
||||
_spi_transfer_delay_ns(delay);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1171,6 +1222,11 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
||||
spi_statistics_add_transfer_stats(statm, xfer, ctlr);
|
||||
spi_statistics_add_transfer_stats(stats, xfer, ctlr);
|
||||
|
||||
if (!ctlr->ptp_sts_supported) {
|
||||
xfer->ptp_sts_word_pre = 0;
|
||||
ptp_read_system_prets(xfer->ptp_sts);
|
||||
}
|
||||
|
||||
if (xfer->tx_buf || xfer->rx_buf) {
|
||||
reinit_completion(&ctlr->xfer_completion);
|
||||
|
||||
@ -1197,13 +1253,17 @@ static int spi_transfer_one_message(struct spi_controller *ctlr,
|
||||
xfer->len);
|
||||
}
|
||||
|
||||
if (!ctlr->ptp_sts_supported) {
|
||||
ptp_read_system_postts(xfer->ptp_sts);
|
||||
xfer->ptp_sts_word_post = xfer->len;
|
||||
}
|
||||
|
||||
trace_spi_transfer_stop(msg, xfer);
|
||||
|
||||
if (msg->status != -EINPROGRESS)
|
||||
goto out;
|
||||
|
||||
if (xfer->delay_usecs)
|
||||
_spi_transfer_delay_ns(xfer->delay_usecs * 1000);
|
||||
spi_transfer_delay_exec(xfer);
|
||||
|
||||
if (xfer->cs_change) {
|
||||
if (list_is_last(&xfer->transfer_list,
|
||||
@ -1265,6 +1325,7 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
|
||||
*/
|
||||
static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
|
||||
{
|
||||
struct spi_transfer *xfer;
|
||||
struct spi_message *msg;
|
||||
bool was_busy = false;
|
||||
unsigned long flags;
|
||||
@ -1391,6 +1452,13 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ctlr->ptp_sts_supported && !ctlr->transfer_one) {
|
||||
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
|
||||
xfer->ptp_sts_word_pre = 0;
|
||||
ptp_read_system_prets(xfer->ptp_sts);
|
||||
}
|
||||
}
|
||||
|
||||
ret = ctlr->transfer_one_message(ctlr, msg);
|
||||
if (ret) {
|
||||
dev_err(&ctlr->dev,
|
||||
@ -1418,6 +1486,99 @@ static void spi_pump_messages(struct kthread_work *work)
|
||||
__spi_pump_messages(ctlr, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_take_timestamp_pre - helper for drivers to collect the beginning of the
|
||||
* TX timestamp for the requested byte from the SPI
|
||||
* transfer. The frequency with which this function
|
||||
* must be called (once per word, once for the whole
|
||||
* transfer, once per batch of words etc) is arbitrary
|
||||
* as long as the @tx buffer offset is greater than or
|
||||
* equal to the requested byte at the time of the
|
||||
* call. The timestamp is only taken once, at the
|
||||
* first such call. It is assumed that the driver
|
||||
* advances its @tx buffer pointer monotonically.
|
||||
* @ctlr: Pointer to the spi_controller structure of the driver
|
||||
* @xfer: Pointer to the transfer being timestamped
|
||||
* @tx: Pointer to the current word within the xfer->tx_buf that the driver is
|
||||
* preparing to transmit right now.
|
||||
* @irqs_off: If true, will disable IRQs and preemption for the duration of the
|
||||
* transfer, for less jitter in time measurement. Only compatible
|
||||
* with PIO drivers. If true, must follow up with
|
||||
* spi_take_timestamp_post or otherwise system will crash.
|
||||
* WARNING: for fully predictable results, the CPU frequency must
|
||||
* also be under control (governor).
|
||||
*/
|
||||
void spi_take_timestamp_pre(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer,
|
||||
const void *tx, bool irqs_off)
|
||||
{
|
||||
u8 bytes_per_word = DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
|
||||
if (!xfer->ptp_sts)
|
||||
return;
|
||||
|
||||
if (xfer->timestamped_pre)
|
||||
return;
|
||||
|
||||
if (tx < (xfer->tx_buf + xfer->ptp_sts_word_pre * bytes_per_word))
|
||||
return;
|
||||
|
||||
/* Capture the resolution of the timestamp */
|
||||
xfer->ptp_sts_word_pre = (tx - xfer->tx_buf) / bytes_per_word;
|
||||
|
||||
xfer->timestamped_pre = true;
|
||||
|
||||
if (irqs_off) {
|
||||
local_irq_save(ctlr->irq_flags);
|
||||
preempt_disable();
|
||||
}
|
||||
|
||||
ptp_read_system_prets(xfer->ptp_sts);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_take_timestamp_pre);
|
||||
|
||||
/**
|
||||
* spi_take_timestamp_post - helper for drivers to collect the end of the
|
||||
* TX timestamp for the requested byte from the SPI
|
||||
* transfer. Can be called with an arbitrary
|
||||
* frequency: only the first call where @tx exceeds
|
||||
* or is equal to the requested word will be
|
||||
* timestamped.
|
||||
* @ctlr: Pointer to the spi_controller structure of the driver
|
||||
* @xfer: Pointer to the transfer being timestamped
|
||||
* @tx: Pointer to the current word within the xfer->tx_buf that the driver has
|
||||
* just transmitted.
|
||||
* @irqs_off: If true, will re-enable IRQs and preemption for the local CPU.
|
||||
*/
|
||||
void spi_take_timestamp_post(struct spi_controller *ctlr,
|
||||
struct spi_transfer *xfer,
|
||||
const void *tx, bool irqs_off)
|
||||
{
|
||||
u8 bytes_per_word = DIV_ROUND_UP(xfer->bits_per_word, 8);
|
||||
|
||||
if (!xfer->ptp_sts)
|
||||
return;
|
||||
|
||||
if (xfer->timestamped_post)
|
||||
return;
|
||||
|
||||
if (tx < (xfer->tx_buf + xfer->ptp_sts_word_post * bytes_per_word))
|
||||
return;
|
||||
|
||||
ptp_read_system_postts(xfer->ptp_sts);
|
||||
|
||||
if (irqs_off) {
|
||||
local_irq_restore(ctlr->irq_flags);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/* Capture the resolution of the timestamp */
|
||||
xfer->ptp_sts_word_post = (tx - xfer->tx_buf) / bytes_per_word;
|
||||
|
||||
xfer->timestamped_post = true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_take_timestamp_post);
|
||||
|
||||
/**
|
||||
* spi_set_thread_rt - set the controller to pump at realtime priority
|
||||
* @ctlr: controller to boost priority of
|
||||
@ -1503,6 +1664,7 @@ EXPORT_SYMBOL_GPL(spi_get_next_queued_message);
|
||||
*/
|
||||
void spi_finalize_current_message(struct spi_controller *ctlr)
|
||||
{
|
||||
struct spi_transfer *xfer;
|
||||
struct spi_message *mesg;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
@ -1511,6 +1673,13 @@ void spi_finalize_current_message(struct spi_controller *ctlr)
|
||||
mesg = ctlr->cur_msg;
|
||||
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
|
||||
|
||||
if (!ctlr->ptp_sts_supported && !ctlr->transfer_one) {
|
||||
list_for_each_entry(xfer, &mesg->transfers, transfer_list) {
|
||||
ptp_read_system_postts(xfer->ptp_sts);
|
||||
xfer->ptp_sts_word_post = xfer->len;
|
||||
}
|
||||
}
|
||||
|
||||
spi_unmap_msg(ctlr, mesg);
|
||||
|
||||
if (ctlr->cur_msg_prepared && ctlr->unprepare_message) {
|
||||
@ -2872,10 +3041,11 @@ struct spi_replaced_transfers *spi_replace_transfers(
|
||||
/* add to list */
|
||||
list_add(&xfer->transfer_list, rxfer->replaced_after);
|
||||
|
||||
/* clear cs_change and delay_usecs for all but the last */
|
||||
/* clear cs_change and delay for all but the last */
|
||||
if (i) {
|
||||
xfer->cs_change = false;
|
||||
xfer->delay_usecs = 0;
|
||||
xfer->delay.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3092,7 +3262,29 @@ int spi_setup(struct spi_device *spi)
|
||||
if (spi->controller->setup)
|
||||
status = spi->controller->setup(spi);
|
||||
|
||||
spi_set_cs(spi, false);
|
||||
if (spi->controller->auto_runtime_pm && spi->controller->set_cs) {
|
||||
status = pm_runtime_get_sync(spi->controller->dev.parent);
|
||||
if (status < 0) {
|
||||
pm_runtime_put_noidle(spi->controller->dev.parent);
|
||||
dev_err(&spi->controller->dev, "Failed to power device: %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* We do not want to return positive value from pm_runtime_get,
|
||||
* there are many instances of devices calling spi_setup() and
|
||||
* checking for a non-zero return value instead of a negative
|
||||
* return value.
|
||||
*/
|
||||
status = 0;
|
||||
|
||||
spi_set_cs(spi, false);
|
||||
pm_runtime_mark_last_busy(spi->controller->dev.parent);
|
||||
pm_runtime_put_autosuspend(spi->controller->dev.parent);
|
||||
} else {
|
||||
spi_set_cs(spi, false);
|
||||
}
|
||||
|
||||
if (spi->rt && !spi->controller->rt) {
|
||||
spi->controller->rt = true;
|
||||
@ -3115,18 +3307,71 @@ EXPORT_SYMBOL_GPL(spi_setup);
|
||||
/**
|
||||
* spi_set_cs_timing - configure CS setup, hold, and inactive delays
|
||||
* @spi: the device that requires specific CS timing configuration
|
||||
* @setup: CS setup time in terms of clock count
|
||||
* @hold: CS hold time in terms of clock count
|
||||
* @inactive_dly: CS inactive delay between transfers in terms of clock count
|
||||
* @setup: CS setup time specified via @spi_delay
|
||||
* @hold: CS hold time specified via @spi_delay
|
||||
* @inactive: CS inactive delay between transfers specified via @spi_delay
|
||||
*
|
||||
* Return: zero on success, else a negative error code.
|
||||
*/
|
||||
void spi_set_cs_timing(struct spi_device *spi, u8 setup, u8 hold,
|
||||
u8 inactive_dly)
|
||||
int spi_set_cs_timing(struct spi_device *spi, struct spi_delay *setup,
|
||||
struct spi_delay *hold, struct spi_delay *inactive)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (spi->controller->set_cs_timing)
|
||||
spi->controller->set_cs_timing(spi, setup, hold, inactive_dly);
|
||||
return spi->controller->set_cs_timing(spi, setup, hold,
|
||||
inactive);
|
||||
|
||||
if ((setup && setup->unit == SPI_DELAY_UNIT_SCK) ||
|
||||
(hold && hold->unit == SPI_DELAY_UNIT_SCK) ||
|
||||
(inactive && inactive->unit == SPI_DELAY_UNIT_SCK)) {
|
||||
dev_err(&spi->dev,
|
||||
"Clock-cycle delays for CS not supported in SW mode\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
len = sizeof(struct spi_delay);
|
||||
|
||||
/* copy delays to controller */
|
||||
if (setup)
|
||||
memcpy(&spi->controller->cs_setup, setup, len);
|
||||
else
|
||||
memset(&spi->controller->cs_setup, 0, len);
|
||||
|
||||
if (hold)
|
||||
memcpy(&spi->controller->cs_hold, hold, len);
|
||||
else
|
||||
memset(&spi->controller->cs_hold, 0, len);
|
||||
|
||||
if (inactive)
|
||||
memcpy(&spi->controller->cs_inactive, inactive, len);
|
||||
else
|
||||
memset(&spi->controller->cs_inactive, 0, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_set_cs_timing);
|
||||
|
||||
static int _spi_xfer_word_delay_update(struct spi_transfer *xfer,
|
||||
struct spi_device *spi)
|
||||
{
|
||||
int delay1, delay2;
|
||||
|
||||
delay1 = spi_delay_to_ns(&xfer->word_delay, xfer);
|
||||
if (delay1 < 0)
|
||||
return delay1;
|
||||
|
||||
delay2 = spi_delay_to_ns(&spi->word_delay, xfer);
|
||||
if (delay2 < 0)
|
||||
return delay2;
|
||||
|
||||
if (delay1 < delay2)
|
||||
memcpy(&xfer->word_delay, &spi->word_delay,
|
||||
sizeof(xfer->word_delay));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
||||
{
|
||||
struct spi_controller *ctlr = spi->controller;
|
||||
@ -3262,8 +3507,8 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (xfer->word_delay_usecs < spi->word_delay_usecs)
|
||||
xfer->word_delay_usecs = spi->word_delay_usecs;
|
||||
if (_spi_xfer_word_delay_update(xfer, spi))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
message->status = -EINPROGRESS;
|
||||
@ -3274,6 +3519,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
||||
static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
||||
{
|
||||
struct spi_controller *ctlr = spi->controller;
|
||||
struct spi_transfer *xfer;
|
||||
|
||||
/*
|
||||
* Some controllers do not support doing regular SPI transfers. Return
|
||||
@ -3289,6 +3535,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
|
||||
|
||||
trace_spi_message_submit(message);
|
||||
|
||||
if (!ctlr->ptp_sts_supported) {
|
||||
list_for_each_entry(xfer, &message->transfers, transfer_list) {
|
||||
xfer->ptp_sts_word_pre = 0;
|
||||
ptp_read_system_prets(xfer->ptp_sts);
|
||||
}
|
||||
}
|
||||
|
||||
return ctlr->transfer(spi, message);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user