Commit 8211e6b8 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'spi/topic/loop' into spi-next

parents c25b2c9e 0732a9d2
...@@ -394,7 +394,7 @@ config SPI_S3C24XX_FIQ ...@@ -394,7 +394,7 @@ config SPI_S3C24XX_FIQ
config SPI_S3C64XX config SPI_S3C64XX
tristate "Samsung S3C64XX series type SPI" tristate "Samsung S3C64XX series type SPI"
depends on (ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5P64X0 || ARCH_EXYNOS) depends on PLAT_SAMSUNG
select S3C64XX_DMA if ARCH_S3C64XX select S3C64XX_DMA if ARCH_S3C64XX
help help
SPI driver for Samsung S3C64XX and newer SoCs. SPI driver for Samsung S3C64XX and newer SoCs.
......
This diff is collapsed.
...@@ -39,6 +39,9 @@ ...@@ -39,6 +39,9 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#define CREATE_TRACE_POINTS
#include <trace/events/spi.h>
static void spidev_release(struct device *dev) static void spidev_release(struct device *dev)
{ {
struct spi_device *spi = to_spi_device(dev); struct spi_device *spi = to_spi_device(dev);
...@@ -525,6 +528,95 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n) ...@@ -525,6 +528,95 @@ int spi_register_board_info(struct spi_board_info const *info, unsigned n)
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
static void spi_set_cs(struct spi_device *spi, bool enable)
{
if (spi->mode & SPI_CS_HIGH)
enable = !enable;
if (spi->cs_gpio >= 0)
gpio_set_value(spi->cs_gpio, !enable);
else if (spi->master->set_cs)
spi->master->set_cs(spi, !enable);
}
/*
* spi_transfer_one_message - Default implementation of transfer_one_message()
*
* This is a standard implementation of transfer_one_message() for
* drivers which impelment a transfer_one() operation. It provides
* standard handling of delays and chip select management.
*/
static int spi_transfer_one_message(struct spi_master *master,
struct spi_message *msg)
{
struct spi_transfer *xfer;
bool cur_cs = true;
bool keep_cs = false;
int ret = 0;
spi_set_cs(msg->spi, true);
list_for_each_entry(xfer, &msg->transfers, transfer_list) {
trace_spi_transfer_start(msg, xfer);
INIT_COMPLETION(master->xfer_completion);
ret = master->transfer_one(master, msg->spi, xfer);
if (ret < 0) {
dev_err(&msg->spi->dev,
"SPI transfer failed: %d\n", ret);
goto out;
}
if (ret > 0)
wait_for_completion(&master->xfer_completion);
trace_spi_transfer_stop(msg, xfer);
if (msg->status != -EINPROGRESS)
goto out;
if (xfer->delay_usecs)
udelay(xfer->delay_usecs);
if (xfer->cs_change) {
if (list_is_last(&xfer->transfer_list,
&msg->transfers)) {
keep_cs = true;
} else {
cur_cs = !cur_cs;
spi_set_cs(msg->spi, cur_cs);
}
}
msg->actual_length += xfer->len;
}
out:
if (ret != 0 || !keep_cs)
spi_set_cs(msg->spi, false);
if (msg->status == -EINPROGRESS)
msg->status = ret;
spi_finalize_current_message(master);
return ret;
}
/**
* spi_finalize_current_transfer - report completion of a transfer
*
* Called by SPI drivers using the core transfer_one_message()
* implementation to notify it that the current interrupt driven
* transfer has finised and the next one may be scheduled.
*/
void spi_finalize_current_transfer(struct spi_master *master)
{
complete(&master->xfer_completion);
}
EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);
/** /**
* spi_pump_messages - kthread work function which processes spi message queue * spi_pump_messages - kthread work function which processes spi message queue
* @work: pointer to kthread work struct contained in the master struct * @work: pointer to kthread work struct contained in the master struct
...@@ -559,6 +651,7 @@ static void spi_pump_messages(struct kthread_work *work) ...@@ -559,6 +651,7 @@ static void spi_pump_messages(struct kthread_work *work)
pm_runtime_mark_last_busy(master->dev.parent); pm_runtime_mark_last_busy(master->dev.parent);
pm_runtime_put_autosuspend(master->dev.parent); pm_runtime_put_autosuspend(master->dev.parent);
} }
trace_spi_master_idle(master);
return; return;
} }
...@@ -587,6 +680,9 @@ static void spi_pump_messages(struct kthread_work *work) ...@@ -587,6 +680,9 @@ static void spi_pump_messages(struct kthread_work *work)
} }
} }
if (!was_busy)
trace_spi_master_busy(master);
if (!was_busy && master->prepare_transfer_hardware) { if (!was_busy && master->prepare_transfer_hardware) {
ret = master->prepare_transfer_hardware(master); ret = master->prepare_transfer_hardware(master);
if (ret) { if (ret) {
...@@ -599,6 +695,20 @@ static void spi_pump_messages(struct kthread_work *work) ...@@ -599,6 +695,20 @@ static void spi_pump_messages(struct kthread_work *work)
} }
} }
trace_spi_message_start(master->cur_msg);
if (master->prepare_message) {
ret = master->prepare_message(master, master->cur_msg);
if (ret) {
dev_err(&master->dev,
"failed to prepare message: %d\n", ret);
master->cur_msg->status = ret;
spi_finalize_current_message(master);
return;
}
master->cur_msg_prepared = true;
}
ret = master->transfer_one_message(master, master->cur_msg); ret = master->transfer_one_message(master, master->cur_msg);
if (ret) { if (ret) {
dev_err(&master->dev, dev_err(&master->dev,
...@@ -680,6 +790,7 @@ void spi_finalize_current_message(struct spi_master *master) ...@@ -680,6 +790,7 @@ void spi_finalize_current_message(struct spi_master *master)
{ {
struct spi_message *mesg; struct spi_message *mesg;
unsigned long flags; unsigned long flags;
int ret;
spin_lock_irqsave(&master->queue_lock, flags); spin_lock_irqsave(&master->queue_lock, flags);
mesg = master->cur_msg; mesg = master->cur_msg;
...@@ -688,9 +799,20 @@ void spi_finalize_current_message(struct spi_master *master) ...@@ -688,9 +799,20 @@ void spi_finalize_current_message(struct spi_master *master)
queue_kthread_work(&master->kworker, &master->pump_messages); queue_kthread_work(&master->kworker, &master->pump_messages);
spin_unlock_irqrestore(&master->queue_lock, flags); spin_unlock_irqrestore(&master->queue_lock, flags);
if (master->cur_msg_prepared && master->unprepare_message) {
ret = master->unprepare_message(master, mesg);
if (ret) {
dev_err(&master->dev,
"failed to unprepare message: %d\n", ret);
}
}
master->cur_msg_prepared = false;
mesg->state = NULL; mesg->state = NULL;
if (mesg->complete) if (mesg->complete)
mesg->complete(mesg->context); mesg->complete(mesg->context);
trace_spi_message_done(mesg);
} }
EXPORT_SYMBOL_GPL(spi_finalize_current_message); EXPORT_SYMBOL_GPL(spi_finalize_current_message);
...@@ -805,6 +927,8 @@ static int spi_master_initialize_queue(struct spi_master *master) ...@@ -805,6 +927,8 @@ static int spi_master_initialize_queue(struct spi_master *master)
master->queued = true; master->queued = true;
master->transfer = spi_queued_transfer; master->transfer = spi_queued_transfer;
if (!master->transfer_one_message)
master->transfer_one_message = spi_transfer_one_message;
/* Initialize and start queue */ /* Initialize and start queue */
ret = spi_init_queue(master); ret = spi_init_queue(master);
...@@ -1205,6 +1329,7 @@ int spi_register_master(struct spi_master *master) ...@@ -1205,6 +1329,7 @@ int spi_register_master(struct spi_master *master)
spin_lock_init(&master->bus_lock_spinlock); spin_lock_init(&master->bus_lock_spinlock);
mutex_init(&master->bus_lock_mutex); mutex_init(&master->bus_lock_mutex);
master->bus_lock_flag = 0; master->bus_lock_flag = 0;
init_completion(&master->xfer_completion);
/* register the device, then userspace will see it. /* register the device, then userspace will see it.
* registration fails if the bus ID is in use. * registration fails if the bus ID is in use.
...@@ -1451,6 +1576,10 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) ...@@ -1451,6 +1576,10 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
struct spi_master *master = spi->master; struct spi_master *master = spi->master;
struct spi_transfer *xfer; struct spi_transfer *xfer;
message->spi = spi;
trace_spi_message_submit(message);
if (list_empty(&message->transfers)) if (list_empty(&message->transfers))
return -EINVAL; return -EINVAL;
if (!message->complete) if (!message->complete)
...@@ -1550,7 +1679,6 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) ...@@ -1550,7 +1679,6 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
} }
} }
message->spi = spi;
message->status = -EINPROGRESS; message->status = -EINPROGRESS;
return master->transfer(spi, message); return master->transfer(spi, message);
} }
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/completion.h>
/* /*
* INTERFACES between SPI master-side drivers and SPI infrastructure. * INTERFACES between SPI master-side drivers and SPI infrastructure.
...@@ -150,8 +151,7 @@ static inline void *spi_get_drvdata(struct spi_device *spi) ...@@ -150,8 +151,7 @@ static inline void *spi_get_drvdata(struct spi_device *spi)
} }
struct spi_message; struct spi_message;
struct spi_transfer;
/** /**
* struct spi_driver - Host side "protocol" driver * struct spi_driver - Host side "protocol" driver
...@@ -257,6 +257,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) ...@@ -257,6 +257,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @queue_lock: spinlock to syncronise access to message queue * @queue_lock: spinlock to syncronise access to message queue
* @queue: message queue * @queue: message queue
* @cur_msg: the currently in-flight message * @cur_msg: the currently in-flight message
* @cur_msg_prepared: spi_prepare_message was called for the currently
* in-flight message
* @xfer_completion: used by core tranfer_one_message()
* @busy: message pump is busy * @busy: message pump is busy
* @running: message pump is running * @running: message pump is running
* @rt: whether this queue is set to run as a realtime task * @rt: whether this queue is set to run as a realtime task
...@@ -274,6 +277,16 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) ...@@ -274,6 +277,16 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @unprepare_transfer_hardware: there are currently no more messages on the * @unprepare_transfer_hardware: there are currently no more messages on the
* queue so the subsystem notifies the driver that it may relax the * queue so the subsystem notifies the driver that it may relax the
* hardware by issuing this call * hardware by issuing this call
* @set_cs: assert or deassert chip select, true to assert. May be called
* from interrupt context.
* @prepare_message: set up the controller to transfer a single message,
* for example doing DMA mapping. Called from threaded
* context.
* @transfer_one: transfer a single spi_transfer. When the
* driver is finished with this transfer it must call
* spi_finalize_current_transfer() so the subsystem can issue
* the next transfer
* @unprepare_message: undo any work done by prepare_message().
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that * number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself). * are not GPIOs (driven by the SPI controller itself).
...@@ -388,11 +401,25 @@ struct spi_master { ...@@ -388,11 +401,25 @@ struct spi_master {
bool running; bool running;
bool rt; bool rt;
bool auto_runtime_pm; bool auto_runtime_pm;
bool cur_msg_prepared;
struct completion xfer_completion;
int (*prepare_transfer_hardware)(struct spi_master *master); int (*prepare_transfer_hardware)(struct spi_master *master);
int (*transfer_one_message)(struct spi_master *master, int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg); struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_master *master); int (*unprepare_transfer_hardware)(struct spi_master *master);
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
/*
* These hooks are for drivers that use a generic implementation
* of transfer_one_message() provied by the core.
*/
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
/* gpio chip select */ /* gpio chip select */
int *cs_gpios; int *cs_gpios;
...@@ -428,6 +455,7 @@ extern int spi_master_resume(struct spi_master *master); ...@@ -428,6 +455,7 @@ extern int spi_master_resume(struct spi_master *master);
/* Calls the driver make to interact with the message queue */ /* Calls the driver make to interact with the message queue */
extern struct spi_message *spi_get_next_queued_message(struct spi_master *master); extern struct spi_message *spi_get_next_queued_message(struct spi_master *master);
extern void spi_finalize_current_message(struct spi_master *master); extern void spi_finalize_current_message(struct spi_master *master);
extern void spi_finalize_current_transfer(struct spi_master *master);
/* the spi driver core manages memory for the spi_master classdev */ /* the spi driver core manages memory for the spi_master classdev */
extern struct spi_master * extern struct spi_master *
......
#undef TRACE_SYSTEM
#define TRACE_SYSTEM spi
#if !defined(_TRACE_SPI_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SPI_H
#include <linux/ktime.h>
#include <linux/tracepoint.h>
DECLARE_EVENT_CLASS(spi_master,
TP_PROTO(struct spi_master *master),
TP_ARGS(master),
TP_STRUCT__entry(
__field( int, bus_num )
),
TP_fast_assign(
__entry->bus_num = master->bus_num;
),
TP_printk("spi%d", (int)__entry->bus_num)
);
DEFINE_EVENT(spi_master, spi_master_idle,
TP_PROTO(struct spi_master *master),
TP_ARGS(master)
);
DEFINE_EVENT(spi_master, spi_master_busy,
TP_PROTO(struct spi_master *master),
TP_ARGS(master)
);
DECLARE_EVENT_CLASS(spi_message,
TP_PROTO(struct spi_message *msg),
TP_ARGS(msg),
TP_STRUCT__entry(
__field( int, bus_num )
__field( int, chip_select )
__field( struct spi_message *, msg )
),
TP_fast_assign(
__entry->bus_num = msg->spi->master->bus_num;
__entry->chip_select = msg->spi->chip_select;
__entry->msg = msg;
),
TP_printk("spi%d.%d %p", (int)__entry->bus_num,
(int)__entry->chip_select,
(struct spi_message *)__entry->msg)
);
DEFINE_EVENT(spi_message, spi_message_submit,
TP_PROTO(struct spi_message *msg),
TP_ARGS(msg)
);
DEFINE_EVENT(spi_message, spi_message_start,
TP_PROTO(struct spi_message *msg),
TP_ARGS(msg)
);
TRACE_EVENT(spi_message_done,
TP_PROTO(struct spi_message *msg),
TP_ARGS(msg),
TP_STRUCT__entry(
__field( int, bus_num )
__field( int, chip_select )
__field( struct spi_message *, msg )
__field( unsigned, frame )
__field( unsigned, actual )
),
TP_fast_assign(
__entry->bus_num = msg->spi->master->bus_num;
__entry->chip_select = msg->spi->chip_select;
__entry->msg = msg;
__entry->frame = msg->frame_length;
__entry->actual = msg->actual_length;
),
TP_printk("spi%d.%d %p len=%u/%u", (int)__entry->bus_num,
(int)__entry->chip_select,
(struct spi_message *)__entry->msg,
(unsigned)__entry->actual, (unsigned)__entry->frame)
);
DECLARE_EVENT_CLASS(spi_transfer,
TP_PROTO(struct spi_message *msg, struct spi_transfer *xfer),
TP_ARGS(msg, xfer),
TP_STRUCT__entry(
__field( int, bus_num )
__field( int, chip_select )
__field( struct spi_transfer *, xfer )
__field( int, len )
),
TP_fast_assign(
__entry->bus_num = msg->spi->master->bus_num;
__entry->chip_select = msg->spi->chip_select;
__entry->xfer = xfer;
__entry->len = xfer->len;
),
TP_printk("spi%d.%d %p len=%d", (int)__entry->bus_num,
(int)__entry->chip_select,
(struct spi_message *)__entry->xfer,
(int)__entry->len)
);
DEFINE_EVENT(spi_transfer, spi_transfer_start,
TP_PROTO(struct spi_message *msg, struct spi_transfer *xfer),
TP_ARGS(msg, xfer)
);
DEFINE_EVENT(spi_transfer, spi_transfer_stop,
TP_PROTO(struct spi_message *msg, struct spi_transfer *xfer),
TP_ARGS(msg, xfer)
);
#endif /* _TRACE_POWER_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment