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
Show 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
...
@@ -205,7 +205,6 @@ struct s3c64xx_spi_driver_data {
...
@@ -205,7 +205,6 @@ struct s3c64xx_spi_driver_data {
#endif
#endif
struct
s3c64xx_spi_port_config
*
port_conf
;
struct
s3c64xx_spi_port_config
*
port_conf
;
unsigned
int
port_id
;
unsigned
int
port_id
;
unsigned
long
gpios
[
4
];
bool
cs_gpio
;
bool
cs_gpio
;
};
};
...
@@ -559,25 +558,18 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
...
@@ -559,25 +558,18 @@ static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
static
inline
void
enable_cs
(
struct
s3c64xx_spi_driver_data
*
sdd
,
static
inline
void
enable_cs
(
struct
s3c64xx_spi_driver_data
*
sdd
,
struct
spi_device
*
spi
)
struct
spi_device
*
spi
)
{
{
struct
s3c64xx_spi_csinfo
*
cs
;
if
(
sdd
->
tgl_spi
!=
NULL
)
{
/* If last device toggled after mssg */
if
(
sdd
->
tgl_spi
!=
NULL
)
{
/* If last device toggled after mssg */
if
(
sdd
->
tgl_spi
!=
spi
)
{
/* if last mssg on diff device */
if
(
sdd
->
tgl_spi
!=
spi
)
{
/* if last mssg on diff device */
/* Deselect the last toggled device */
/* Deselect the last toggled device */
cs
=
sdd
->
tgl_spi
->
controller_data
;
if
(
spi
->
cs_gpio
>=
0
)
if
(
sdd
->
cs_gpio
)
gpio_set_value
(
spi
->
cs_gpio
,
gpio_set_value
(
cs
->
line
,
spi
->
mode
&
SPI_CS_HIGH
?
0
:
1
);
spi
->
mode
&
SPI_CS_HIGH
?
0
:
1
);
}
}
sdd
->
tgl_spi
=
NULL
;
sdd
->
tgl_spi
=
NULL
;
}
}
cs
=
spi
->
controller_data
;
if
(
spi
->
cs_gpio
>=
0
)
if
(
sdd
->
cs_gpio
)
gpio_set_value
(
spi
->
cs_gpio
,
spi
->
mode
&
SPI_CS_HIGH
?
1
:
0
);
gpio_set_value
(
cs
->
line
,
spi
->
mode
&
SPI_CS_HIGH
?
1
:
0
);
/* Start the signals */
writel
(
0
,
sdd
->
regs
+
S3C64XX_SPI_SLAVE_SEL
);
}
}
static
u32
s3c64xx_spi_wait_for_timeout
(
struct
s3c64xx_spi_driver_data
*
sdd
,
static
u32
s3c64xx_spi_wait_for_timeout
(
struct
s3c64xx_spi_driver_data
*
sdd
,
...
@@ -702,16 +694,11 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
...
@@ -702,16 +694,11 @@ static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd,
static
inline
void
disable_cs
(
struct
s3c64xx_spi_driver_data
*
sdd
,
static
inline
void
disable_cs
(
struct
s3c64xx_spi_driver_data
*
sdd
,
struct
spi_device
*
spi
)
struct
spi_device
*
spi
)
{
{
struct
s3c64xx_spi_csinfo
*
cs
=
spi
->
controller_data
;
if
(
sdd
->
tgl_spi
==
spi
)
if
(
sdd
->
tgl_spi
==
spi
)
sdd
->
tgl_spi
=
NULL
;
sdd
->
tgl_spi
=
NULL
;
if
(
sdd
->
cs_gpio
)
if
(
spi
->
cs_gpio
>=
0
)
gpio_set_value
(
cs
->
line
,
spi
->
mode
&
SPI_CS_HIGH
?
0
:
1
);
gpio_set_value
(
spi
->
cs_gpio
,
spi
->
mode
&
SPI_CS_HIGH
?
0
:
1
);
/* Quiese the signals */
writel
(
S3C64XX_SPI_SLAVE_SIG_INACT
,
sdd
->
regs
+
S3C64XX_SPI_SLAVE_SEL
);
}
}
static
void
s3c64xx_spi_config
(
struct
s3c64xx_spi_driver_data
*
sdd
)
static
void
s3c64xx_spi_config
(
struct
s3c64xx_spi_driver_data
*
sdd
)
...
@@ -862,16 +849,12 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
...
@@ -862,16 +849,12 @@ static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd,
}
}
}
}
static
int
s3c64xx_spi_
transfer_on
e_message
(
struct
spi_master
*
master
,
static
int
s3c64xx_spi_
prepar
e_message
(
struct
spi_master
*
master
,
struct
spi_message
*
msg
)
struct
spi_message
*
msg
)
{
{
struct
s3c64xx_spi_driver_data
*
sdd
=
spi_master_get_devdata
(
master
);
struct
s3c64xx_spi_driver_data
*
sdd
=
spi_master_get_devdata
(
master
);
struct
spi_device
*
spi
=
msg
->
spi
;
struct
spi_device
*
spi
=
msg
->
spi
;
struct
s3c64xx_spi_csinfo
*
cs
=
spi
->
controller_data
;
struct
s3c64xx_spi_csinfo
*
cs
=
spi
->
controller_data
;
struct
spi_transfer
*
xfer
;
int
status
=
0
,
cs_toggle
=
0
;
u32
speed
;
u8
bpw
;
/* If Master's(controller) state differs from that needed by Slave */
/* If Master's(controller) state differs from that needed by Slave */
if
(
sdd
->
cur_speed
!=
spi
->
max_speed_hz
if
(
sdd
->
cur_speed
!=
spi
->
max_speed_hz
...
@@ -887,15 +870,23 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
...
@@ -887,15 +870,23 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
if
(
s3c64xx_spi_map_mssg
(
sdd
,
msg
))
{
if
(
s3c64xx_spi_map_mssg
(
sdd
,
msg
))
{
dev_err
(
&
spi
->
dev
,
dev_err
(
&
spi
->
dev
,
"Xfer: Unable to map message buffers!
\n
"
);
"Xfer: Unable to map message buffers!
\n
"
);
status
=
-
ENOMEM
;
return
-
ENOMEM
;
goto
out
;
}
}
/* Configure feedback delay */
/* Configure feedback delay */
writel
(
cs
->
fb_delay
&
0x3
,
sdd
->
regs
+
S3C64XX_SPI_FB_CLK
);
writel
(
cs
->
fb_delay
&
0x3
,
sdd
->
regs
+
S3C64XX_SPI_FB_CLK
);
list_for_each_entry
(
xfer
,
&
msg
->
transfers
,
transfer_list
)
{
return
0
;
}
static
int
s3c64xx_spi_transfer_one
(
struct
spi_master
*
master
,
struct
spi_device
*
spi
,
struct
spi_transfer
*
xfer
)
{
struct
s3c64xx_spi_driver_data
*
sdd
=
spi_master_get_devdata
(
master
);
int
status
;
u32
speed
;
u8
bpw
;
unsigned
long
flags
;
unsigned
long
flags
;
int
use_dma
;
int
use_dma
;
...
@@ -909,8 +900,7 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
...
@@ -909,8 +900,7 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
dev_err
(
&
spi
->
dev
,
dev_err
(
&
spi
->
dev
,
"Xfer length(%u) not a multiple of word size(%u)
\n
"
,
"Xfer length(%u) not a multiple of word size(%u)
\n
"
,
xfer
->
len
,
bpw
/
8
);
xfer
->
len
,
bpw
/
8
);
status
=
-
EIO
;
return
-
EIO
;
goto
out
;
}
}
if
(
bpw
!=
sdd
->
cur_bpw
||
speed
!=
sdd
->
cur_speed
)
{
if
(
bpw
!=
sdd
->
cur_bpw
||
speed
!=
sdd
->
cur_speed
)
{
...
@@ -934,8 +924,11 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
...
@@ -934,8 +924,11 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
enable_datapath
(
sdd
,
spi
,
xfer
,
use_dma
);
enable_datapath
(
sdd
,
spi
,
xfer
,
use_dma
);
/* Slave Select */
/* Start the signals */
enable_cs
(
sdd
,
spi
);
writel
(
0
,
sdd
->
regs
+
S3C64XX_SPI_SLAVE_SEL
);
/* Start the signals */
writel
(
0
,
sdd
->
regs
+
S3C64XX_SPI_SLAVE_SEL
);
spin_unlock_irqrestore
(
&
sdd
->
lock
,
flags
);
spin_unlock_irqrestore
(
&
sdd
->
lock
,
flags
);
...
@@ -956,37 +949,19 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
...
@@ -956,37 +949,19 @@ static int s3c64xx_spi_transfer_one_message(struct spi_master *master,
&&
(
sdd
->
state
&
RXBUSY
))
&&
(
sdd
->
state
&
RXBUSY
))
s3c64xx_spi_dma_stop
(
sdd
,
&
sdd
->
rx_dma
);
s3c64xx_spi_dma_stop
(
sdd
,
&
sdd
->
rx_dma
);
}
}
}
else
{
goto
out
;
}
if
(
xfer
->
delay_usecs
)
udelay
(
xfer
->
delay_usecs
);
if
(
xfer
->
cs_change
)
{
/* Hint that the next mssg is gonna be
for the same device */
if
(
list_is_last
(
&
xfer
->
transfer_list
,
&
msg
->
transfers
))
cs_toggle
=
1
;
}
msg
->
actual_length
+=
xfer
->
len
;
flush_fifo
(
sdd
);
flush_fifo
(
sdd
);
}
}
out:
return
status
;
if
(
!
cs_toggle
||
status
)
}
disable_cs
(
sdd
,
spi
);
else
sdd
->
tgl_spi
=
spi
;
s3c64xx_spi_unmap_mssg
(
sdd
,
msg
);
msg
->
status
=
status
;
static
int
s3c64xx_spi_unprepare_message
(
struct
spi_master
*
master
,
struct
spi_message
*
msg
)
{
struct
s3c64xx_spi_driver_data
*
sdd
=
spi_master_get_devdata
(
master
);
s
pi_finalize_current_message
(
master
);
s
3c64xx_spi_unmap_mssg
(
sdd
,
msg
);
return
0
;
return
0
;
}
}
...
@@ -1071,6 +1046,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
...
@@ -1071,6 +1046,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
cs
->
line
,
err
);
cs
->
line
,
err
);
goto
err_gpio_req
;
goto
err_gpio_req
;
}
}
spi
->
cs_gpio
=
cs
->
line
;
}
}
spi_set_ctldata
(
spi
,
cs
);
spi_set_ctldata
(
spi
,
cs
);
...
@@ -1117,12 +1094,14 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
...
@@ -1117,12 +1094,14 @@ static int s3c64xx_spi_setup(struct spi_device *spi)
}
}
pm_runtime_put
(
&
sdd
->
pdev
->
dev
);
pm_runtime_put
(
&
sdd
->
pdev
->
dev
);
writel
(
S3C64XX_SPI_SLAVE_SIG_INACT
,
sdd
->
regs
+
S3C64XX_SPI_SLAVE_SEL
);
disable_cs
(
sdd
,
spi
);
disable_cs
(
sdd
,
spi
);
return
0
;
return
0
;
setup_exit:
setup_exit:
pm_runtime_put
(
&
sdd
->
pdev
->
dev
);
pm_runtime_put
(
&
sdd
->
pdev
->
dev
);
/* setup() returns with device de-selected */
/* setup() returns with device de-selected */
writel
(
S3C64XX_SPI_SLAVE_SIG_INACT
,
sdd
->
regs
+
S3C64XX_SPI_SLAVE_SEL
);
disable_cs
(
sdd
,
spi
);
disable_cs
(
sdd
,
spi
);
gpio_free
(
cs
->
line
);
gpio_free
(
cs
->
line
);
...
@@ -1141,8 +1120,8 @@ static void s3c64xx_spi_cleanup(struct spi_device *spi)
...
@@ -1141,8 +1120,8 @@ static void s3c64xx_spi_cleanup(struct spi_device *spi)
struct
s3c64xx_spi_driver_data
*
sdd
;
struct
s3c64xx_spi_driver_data
*
sdd
;
sdd
=
spi_master_get_devdata
(
spi
->
master
);
sdd
=
spi_master_get_devdata
(
spi
->
master
);
if
(
cs
&&
sdd
->
cs_gpio
)
{
if
(
spi
->
cs_gpio
)
{
gpio_free
(
cs
->
line
);
gpio_free
(
spi
->
cs_gpio
);
if
(
spi
->
dev
.
of_node
)
if
(
spi
->
dev
.
of_node
)
kfree
(
cs
);
kfree
(
cs
);
}
}
...
@@ -1360,7 +1339,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
...
@@ -1360,7 +1339,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
master
->
setup
=
s3c64xx_spi_setup
;
master
->
setup
=
s3c64xx_spi_setup
;
master
->
cleanup
=
s3c64xx_spi_cleanup
;
master
->
cleanup
=
s3c64xx_spi_cleanup
;
master
->
prepare_transfer_hardware
=
s3c64xx_spi_prepare_transfer
;
master
->
prepare_transfer_hardware
=
s3c64xx_spi_prepare_transfer
;
master
->
transfer_one_message
=
s3c64xx_spi_transfer_one_message
;
master
->
prepare_message
=
s3c64xx_spi_prepare_message
;
master
->
transfer_one
=
s3c64xx_spi_transfer_one
;
master
->
unprepare_message
=
s3c64xx_spi_unprepare_message
;
master
->
unprepare_transfer_hardware
=
s3c64xx_spi_unprepare_transfer
;
master
->
unprepare_transfer_hardware
=
s3c64xx_spi_unprepare_transfer
;
master
->
num_chipselect
=
sci
->
num_cs
;
master
->
num_chipselect
=
sci
->
num_cs
;
master
->
dma_alignment
=
8
;
master
->
dma_alignment
=
8
;
...
@@ -1432,9 +1413,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
...
@@ -1432,9 +1413,9 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
pm_runtime_set_active
(
&
pdev
->
dev
);
pm_runtime_set_active
(
&
pdev
->
dev
);
pm_runtime_enable
(
&
pdev
->
dev
);
pm_runtime_enable
(
&
pdev
->
dev
);
if
(
spi_register_master
(
master
))
{
ret
=
devm_spi_register_master
(
&
pdev
->
dev
,
master
);
dev_err
(
&
pdev
->
dev
,
"cannot register SPI master
\n
"
);
if
(
ret
!=
0
)
{
ret
=
-
EBUSY
;
dev_err
(
&
pdev
->
dev
,
"cannot register SPI master: %d
\n
"
,
ret
)
;
goto
err3
;
goto
err3
;
}
}
...
@@ -1463,16 +1444,12 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
...
@@ -1463,16 +1444,12 @@ static int s3c64xx_spi_remove(struct platform_device *pdev)
pm_runtime_disable
(
&
pdev
->
dev
);
pm_runtime_disable
(
&
pdev
->
dev
);
spi_unregister_master
(
master
);
writel
(
0
,
sdd
->
regs
+
S3C64XX_SPI_INT_EN
);
writel
(
0
,
sdd
->
regs
+
S3C64XX_SPI_INT_EN
);
clk_disable_unprepare
(
sdd
->
src_clk
);
clk_disable_unprepare
(
sdd
->
src_clk
);
clk_disable_unprepare
(
sdd
->
clk
);
clk_disable_unprepare
(
sdd
->
clk
);
spi_master_put
(
master
);
return
0
;
return
0
;
}
}
...
@@ -1530,9 +1507,17 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
...
@@ -1530,9 +1507,17 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
{
{
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
struct
s3c64xx_spi_driver_data
*
sdd
=
spi_master_get_devdata
(
master
);
struct
s3c64xx_spi_driver_data
*
sdd
=
spi_master_get_devdata
(
master
);
int
ret
;
clk_prepare_enable
(
sdd
->
src_clk
);
ret
=
clk_prepare_enable
(
sdd
->
src_clk
);
clk_prepare_enable
(
sdd
->
clk
);
if
(
ret
!=
0
)
return
ret
;
ret
=
clk_prepare_enable
(
sdd
->
clk
);
if
(
ret
!=
0
)
{
clk_disable_unprepare
(
sdd
->
src_clk
);
return
ret
;
}
return
0
;
return
0
;
}
}
...
@@ -1618,6 +1603,18 @@ static struct platform_device_id s3c64xx_spi_driver_ids[] = {
...
@@ -1618,6 +1603,18 @@ static struct platform_device_id s3c64xx_spi_driver_ids[] = {
};
};
static
const
struct
of_device_id
s3c64xx_spi_dt_match
[]
=
{
static
const
struct
of_device_id
s3c64xx_spi_dt_match
[]
=
{
{
.
compatible
=
"samsung,s3c2443-spi"
,
.
data
=
(
void
*
)
&
s3c2443_spi_port_config
,
},
{
.
compatible
=
"samsung,s3c6410-spi"
,
.
data
=
(
void
*
)
&
s3c6410_spi_port_config
,
},
{
.
compatible
=
"samsung,s5pc100-spi"
,
.
data
=
(
void
*
)
&
s5pc100_spi_port_config
,
},
{
.
compatible
=
"samsung,s5pv210-spi"
,
.
data
=
(
void
*
)
&
s5pv210_spi_port_config
,
},
{
.
compatible
=
"samsung,exynos4210-spi"
,
{
.
compatible
=
"samsung,exynos4210-spi"
,
.
data
=
(
void
*
)
&
exynos4_spi_port_config
,
.
data
=
(
void
*
)
&
exynos4_spi_port_config
,
},
},
...
@@ -1635,22 +1632,13 @@ static struct platform_driver s3c64xx_spi_driver = {
...
@@ -1635,22 +1632,13 @@ static struct platform_driver s3c64xx_spi_driver = {
.
pm
=
&
s3c64xx_spi_pm
,
.
pm
=
&
s3c64xx_spi_pm
,
.
of_match_table
=
of_match_ptr
(
s3c64xx_spi_dt_match
),
.
of_match_table
=
of_match_ptr
(
s3c64xx_spi_dt_match
),
},
},
.
probe
=
s3c64xx_spi_probe
,
.
remove
=
s3c64xx_spi_remove
,
.
remove
=
s3c64xx_spi_remove
,
.
id_table
=
s3c64xx_spi_driver_ids
,
.
id_table
=
s3c64xx_spi_driver_ids
,
};
};
MODULE_ALIAS
(
"platform:s3c64xx-spi"
);
MODULE_ALIAS
(
"platform:s3c64xx-spi"
);
static
int
__init
s3c64xx_spi_init
(
void
)
module_platform_driver
(
s3c64xx_spi_driver
);
{
return
platform_driver_probe
(
&
s3c64xx_spi_driver
,
s3c64xx_spi_probe
);
}
subsys_initcall
(
s3c64xx_spi_init
);
static
void
__exit
s3c64xx_spi_exit
(
void
)
{
platform_driver_unregister
(
&
s3c64xx_spi_driver
);
}
module_exit
(
s3c64xx_spi_exit
);
MODULE_AUTHOR
(
"Jaswinder Singh <jassi.brar@samsung.com>"
);
MODULE_AUTHOR
(
"Jaswinder Singh <jassi.brar@samsung.com>"
);
MODULE_DESCRIPTION
(
"S3C64XX SPI Controller Driver"
);
MODULE_DESCRIPTION
(
"S3C64XX SPI Controller Driver"
);
...
...
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