Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
8211e6b8
Commit
8211e6b8
authored
Oct 25, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'spi/topic/loop' into spi-next
parents
c25b2c9e
0732a9d2
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
428 additions
and
128 deletions
+428
-128
drivers/spi/Kconfig
drivers/spi/Kconfig
+1
-1
drivers/spi/spi-s3c64xx.c
drivers/spi/spi-s3c64xx.c
+112
-124
drivers/spi/spi.c
drivers/spi/spi.c
+129
-1
include/linux/spi/spi.h
include/linux/spi/spi.h
+30
-2
include/trace/events/spi.h
include/trace/events/spi.h
+156
-0
No files found.
drivers/spi/Kconfig
View file @
8211e6b8
...
@@ -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.
...
...
drivers/spi/spi-s3c64xx.c
View file @
8211e6b8
This diff is collapsed.
Click to expand it.
drivers/spi/spi.c
View file @
8211e6b8
...
@@ -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
);
}
}
...
...
include/linux/spi/spi.h
View file @
8211e6b8
...
@@ -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
*
...
...
include/trace/events/spi.h
0 → 100644
View file @
8211e6b8
#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>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment