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
95e40cc5
Commit
95e40cc5
authored
Jun 26, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'spi/topic/mpc512x' into spi-next
parents
11e91689
85085898
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
186 additions
and
155 deletions
+186
-155
drivers/spi/spi-mpc512x-psc.c
drivers/spi/spi-mpc512x-psc.c
+186
-155
No files found.
drivers/spi/spi-mpc512x-psc.c
View file @
95e40cc5
...
...
@@ -21,7 +21,6 @@
#include <linux/interrupt.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
#include <linux/io.h>
#include <linux/delay.h>
...
...
@@ -33,24 +32,15 @@
struct
mpc512x_psc_spi
{
void
(
*
cs_control
)(
struct
spi_device
*
spi
,
bool
on
);
u32
sysclk
;
/* driver internal data */
struct
mpc52xx_psc
__iomem
*
psc
;
struct
mpc512x_psc_fifo
__iomem
*
fifo
;
unsigned
int
irq
;
u8
bits_per_word
;
u8
busy
;
u32
mclk
;
u8
eofbyte
;
struct
workqueue_struct
*
workqueue
;
struct
work_struct
work
;
struct
list_head
queue
;
spinlock_t
lock
;
/* Message queue lock */
struct
completion
done
;
struct
completion
txisrdone
;
};
/* controller state */
...
...
@@ -136,145 +126,223 @@ static int mpc512x_psc_spi_transfer_rxtx(struct spi_device *spi,
struct
spi_transfer
*
t
)
{
struct
mpc512x_psc_spi
*
mps
=
spi_master_get_devdata
(
spi
->
master
);
struct
mpc52xx_psc
__iomem
*
psc
=
mps
->
psc
;
struct
mpc512x_psc_fifo
__iomem
*
fifo
=
mps
->
fifo
;
size_t
len
=
t
->
len
;
size_t
tx_len
=
t
->
len
;
size_t
rx_len
=
t
->
len
;
u8
*
tx_buf
=
(
u8
*
)
t
->
tx_buf
;
u8
*
rx_buf
=
(
u8
*
)
t
->
rx_buf
;
if
(
!
tx_buf
&&
!
rx_buf
&&
t
->
len
)
return
-
EINVAL
;
/* Zero MR2 */
in_8
(
&
psc
->
mode
);
out_8
(
&
psc
->
mode
,
0x0
);
/* enable transmiter/receiver */
out_8
(
&
psc
->
command
,
MPC52xx_PSC_TX_ENABLE
|
MPC52xx_PSC_RX_ENABLE
);
while
(
len
)
{
int
count
;
int
i
;
while
(
rx_len
||
tx_len
)
{
size_t
txcount
;
u8
data
;
size_t
fifosz
;
int
rxcount
;
size_t
rxcount
;
int
rxtries
;
/*
*
The number of bytes that can be sent at a tim
e
*
depends on the fifo size.
*
send the TX bytes in as large a chunk as possibl
e
*
but neither exceed the TX nor the RX FIFOs
*/
fifosz
=
MPC512x_PSC_FIFO_SZ
(
in_be32
(
&
fifo
->
txsz
));
count
=
min
(
fifosz
,
len
);
for
(
i
=
count
;
i
>
0
;
i
--
)
{
data
=
tx_buf
?
*
tx_buf
++
:
0
;
if
(
len
==
EOFBYTE
&&
t
->
cs_change
)
setbits32
(
&
fifo
->
txcmd
,
MPC512x_PSC_FIFO_EOF
);
out_8
(
&
fifo
->
txdata_8
,
data
);
len
--
;
txcount
=
min
(
fifosz
,
tx_len
);
fifosz
=
MPC512x_PSC_FIFO_SZ
(
in_be32
(
&
fifo
->
rxsz
));
fifosz
-=
in_be32
(
&
fifo
->
rxcnt
)
+
1
;
txcount
=
min
(
fifosz
,
txcount
);
if
(
txcount
)
{
/* fill the TX FIFO */
while
(
txcount
--
>
0
)
{
data
=
tx_buf
?
*
tx_buf
++
:
0
;
if
(
tx_len
==
EOFBYTE
&&
t
->
cs_change
)
setbits32
(
&
fifo
->
txcmd
,
MPC512x_PSC_FIFO_EOF
);
out_8
(
&
fifo
->
txdata_8
,
data
);
tx_len
--
;
}
/* have the ISR trigger when the TX FIFO is empty */
INIT_COMPLETION
(
mps
->
txisrdone
);
out_be32
(
&
fifo
->
txisr
,
MPC512x_PSC_FIFO_EMPTY
);
out_be32
(
&
fifo
->
tximr
,
MPC512x_PSC_FIFO_EMPTY
);
wait_for_completion
(
&
mps
->
txisrdone
);
}
INIT_COMPLETION
(
mps
->
done
);
/*
* consume as much RX data as the FIFO holds, while we
* iterate over the transfer's TX data length
*
* only insist in draining all the remaining RX bytes
* when the TX bytes were exhausted (that's at the very
* end of this transfer, not when still iterating over
* the transfer's chunks)
*/
rxtries
=
50
;
do
{
/*
* grab whatever was in the FIFO when we started
* looking, don't bother fetching what was added to
* the FIFO while we read from it -- we'll return
* here eventually and prefer sending out remaining
* TX data
*/
fifosz
=
in_be32
(
&
fifo
->
rxcnt
);
rxcount
=
min
(
fifosz
,
rx_len
);
while
(
rxcount
--
>
0
)
{
data
=
in_8
(
&
fifo
->
rxdata_8
);
if
(
rx_buf
)
*
rx_buf
++
=
data
;
rx_len
--
;
}
/* interrupt on tx fifo empty */
out_be32
(
&
fifo
->
txisr
,
MPC512x_PSC_FIFO_EMPTY
);
out_be32
(
&
fifo
->
tximr
,
MPC512x_PSC_FIFO_EMPTY
);
/*
* come back later if there still is TX data to send,
* bail out of the RX drain loop if all of the TX data
* was sent and all of the RX data was received (i.e.
* when the transmission has completed)
*/
if
(
tx_len
)
break
;
if
(
!
rx_len
)
break
;
wait_for_completion
(
&
mps
->
done
);
/*
* TX data transmission has completed while RX data
* is still pending -- that's a transient situation
* which depends on wire speed and specific
* hardware implementation details (buffering) yet
* should resolve very quickly
*
* just yield for a moment to not hog the CPU for
* too long when running SPI at low speed
*
* the timeout range is rather arbitrary and tries
* to balance throughput against system load; the
* chosen values result in a minimal timeout of 50
* times 10us and thus work at speeds as low as
* some 20kbps, while the maximum timeout at the
* transfer's end could be 5ms _if_ nothing else
* ticks in the system _and_ RX data still wasn't
* received, which only occurs in situations that
* are exceptional; removing the unpredictability
* of the timeout either decreases throughput
* (longer timeouts), or puts more load on the
* system (fixed short timeouts) or requires the
* use of a timeout API instead of a counter and an
* unknown inner delay
*/
usleep_range
(
10
,
100
);
}
while
(
--
rxtries
>
0
);
if
(
!
tx_len
&&
rx_len
&&
!
rxtries
)
{
/*
* not enough RX bytes even after several retries
* and the resulting rather long timeout?
*/
rxcount
=
in_be32
(
&
fifo
->
rxcnt
);
dev_warn
(
&
spi
->
dev
,
"short xfer, missing %zd RX bytes, FIFO level %zd
\n
"
,
rx_len
,
rxcount
);
}
mdelay
(
1
);
/*
* drain and drop RX data which "should not be there" in
* the first place, for undisturbed transmission this turns
* into a NOP (except for the FIFO level fetch)
*/
if
(
!
tx_len
&&
!
rx_len
)
{
while
(
in_be32
(
&
fifo
->
rxcnt
))
in_8
(
&
fifo
->
rxdata_8
);
}
/* rx fifo should have count bytes in it */
rxcount
=
in_be32
(
&
fifo
->
rxcnt
);
if
(
rxcount
!=
count
)
mdelay
(
1
);
}
return
0
;
}
rxcount
=
in_be32
(
&
fifo
->
rxcnt
);
if
(
rxcount
!=
count
)
{
dev_warn
(
&
spi
->
dev
,
"expected %d bytes in rx fifo "
"but got %d
\n
"
,
count
,
rxcount
);
static
int
mpc512x_psc_spi_msg_xfer
(
struct
spi_master
*
master
,
struct
spi_message
*
m
)
{
struct
spi_device
*
spi
;
unsigned
cs_change
;
int
status
;
struct
spi_transfer
*
t
;
spi
=
m
->
spi
;
cs_change
=
1
;
status
=
0
;
list_for_each_entry
(
t
,
&
m
->
transfers
,
transfer_list
)
{
if
(
t
->
bits_per_word
||
t
->
speed_hz
)
{
status
=
mpc512x_psc_spi_transfer_setup
(
spi
,
t
);
if
(
status
<
0
)
break
;
}
rxcount
=
min
(
rxcount
,
count
);
for
(
i
=
rxcount
;
i
>
0
;
i
--
)
{
data
=
in_8
(
&
fifo
->
rxdata_8
);
if
(
rx_buf
)
*
rx_buf
++
=
data
;
}
while
(
in_be32
(
&
fifo
->
rxcnt
))
{
in_8
(
&
fifo
->
rxdata_8
);
}
if
(
cs_change
)
mpc512x_psc_spi_activate_cs
(
spi
);
cs_change
=
t
->
cs_change
;
status
=
mpc512x_psc_spi_transfer_rxtx
(
spi
,
t
);
if
(
status
)
break
;
m
->
actual_length
+=
t
->
len
;
if
(
t
->
delay_usecs
)
udelay
(
t
->
delay_usecs
);
if
(
cs_change
)
mpc512x_psc_spi_deactivate_cs
(
spi
);
}
/* disable transmiter/receiver and fifo interrupt */
out_8
(
&
psc
->
command
,
MPC52xx_PSC_TX_DISABLE
|
MPC52xx_PSC_RX_DISABLE
);
out_be32
(
&
fifo
->
tximr
,
0
);
return
0
;
m
->
status
=
status
;
m
->
complete
(
m
->
context
);
if
(
status
||
!
cs_change
)
mpc512x_psc_spi_deactivate_cs
(
spi
);
mpc512x_psc_spi_transfer_setup
(
spi
,
NULL
);
spi_finalize_current_message
(
master
);
return
status
;
}
static
void
mpc512x_psc_spi_work
(
struct
work_struct
*
work
)
static
int
mpc512x_psc_spi_prep_xfer_hw
(
struct
spi_master
*
master
)
{
struct
mpc512x_psc_spi
*
mps
=
container_of
(
work
,
struct
mpc512x_psc_spi
,
work
);
spin_lock_irq
(
&
mps
->
lock
);
mps
->
busy
=
1
;
while
(
!
list_empty
(
&
mps
->
queue
))
{
struct
spi_message
*
m
;
struct
spi_device
*
spi
;
struct
spi_transfer
*
t
=
NULL
;
unsigned
cs_change
;
int
status
;
m
=
container_of
(
mps
->
queue
.
next
,
struct
spi_message
,
queue
);
list_del_init
(
&
m
->
queue
);
spin_unlock_irq
(
&
mps
->
lock
);
spi
=
m
->
spi
;
cs_change
=
1
;
status
=
0
;
list_for_each_entry
(
t
,
&
m
->
transfers
,
transfer_list
)
{
if
(
t
->
bits_per_word
||
t
->
speed_hz
)
{
status
=
mpc512x_psc_spi_transfer_setup
(
spi
,
t
);
if
(
status
<
0
)
break
;
}
struct
mpc512x_psc_spi
*
mps
=
spi_master_get_devdata
(
master
);
struct
mpc52xx_psc
__iomem
*
psc
=
mps
->
psc
;
if
(
cs_change
)
mpc512x_psc_spi_activate_cs
(
spi
);
cs_change
=
t
->
cs_change
;
dev_dbg
(
&
master
->
dev
,
"%s()
\n
"
,
__func__
);
status
=
mpc512x_psc_spi_transfer_rxtx
(
spi
,
t
);
if
(
status
)
break
;
m
->
actual_length
+=
t
->
len
;
/* Zero MR2 */
in_8
(
&
psc
->
mode
);
out_8
(
&
psc
->
mode
,
0x0
);
if
(
t
->
delay_usecs
)
udelay
(
t
->
delay_usecs
);
/* enable transmitter/receiver */
out_8
(
&
psc
->
command
,
MPC52xx_PSC_TX_ENABLE
|
MPC52xx_PSC_RX_ENABLE
);
if
(
cs_change
)
mpc512x_psc_spi_deactivate_cs
(
spi
);
}
return
0
;
}
m
->
status
=
status
;
m
->
complete
(
m
->
context
);
static
int
mpc512x_psc_spi_unprep_xfer_hw
(
struct
spi_master
*
master
)
{
struct
mpc512x_psc_spi
*
mps
=
spi_master_get_devdata
(
master
);
struct
mpc52xx_psc
__iomem
*
psc
=
mps
->
psc
;
struct
mpc512x_psc_fifo
__iomem
*
fifo
=
mps
->
fifo
;
if
(
status
||
!
cs_change
)
mpc512x_psc_spi_deactivate_cs
(
spi
);
dev_dbg
(
&
master
->
dev
,
"%s()
\n
"
,
__func__
);
mpc512x_psc_spi_transfer_setup
(
spi
,
NULL
);
/* disable transmitter/receiver and fifo interrupt */
out_8
(
&
psc
->
command
,
MPC52xx_PSC_TX_DISABLE
|
MPC52xx_PSC_RX_DISABLE
);
out_be32
(
&
fifo
->
tximr
,
0
);
spin_lock_irq
(
&
mps
->
lock
);
}
mps
->
busy
=
0
;
spin_unlock_irq
(
&
mps
->
lock
);
return
0
;
}
static
int
mpc512x_psc_spi_setup
(
struct
spi_device
*
spi
)
{
struct
mpc512x_psc_spi
*
mps
=
spi_master_get_devdata
(
spi
->
master
);
struct
mpc512x_psc_spi_cs
*
cs
=
spi
->
controller_state
;
unsigned
long
flags
;
int
ret
;
if
(
spi
->
bits_per_word
%
8
)
...
...
@@ -303,28 +371,6 @@ static int mpc512x_psc_spi_setup(struct spi_device *spi)
cs
->
bits_per_word
=
spi
->
bits_per_word
;
cs
->
speed_hz
=
spi
->
max_speed_hz
;
spin_lock_irqsave
(
&
mps
->
lock
,
flags
);
if
(
!
mps
->
busy
)
mpc512x_psc_spi_deactivate_cs
(
spi
);
spin_unlock_irqrestore
(
&
mps
->
lock
,
flags
);
return
0
;
}
static
int
mpc512x_psc_spi_transfer
(
struct
spi_device
*
spi
,
struct
spi_message
*
m
)
{
struct
mpc512x_psc_spi
*
mps
=
spi_master_get_devdata
(
spi
->
master
);
unsigned
long
flags
;
m
->
actual_length
=
0
;
m
->
status
=
-
EINPROGRESS
;
spin_lock_irqsave
(
&
mps
->
lock
,
flags
);
list_add_tail
(
&
m
->
queue
,
&
mps
->
queue
);
queue_work
(
mps
->
workqueue
,
&
mps
->
work
);
spin_unlock_irqrestore
(
&
mps
->
lock
,
flags
);
return
0
;
}
...
...
@@ -407,12 +453,12 @@ static irqreturn_t mpc512x_psc_spi_isr(int irq, void *dev_id)
struct
mpc512x_psc_spi
*
mps
=
(
struct
mpc512x_psc_spi
*
)
dev_id
;
struct
mpc512x_psc_fifo
__iomem
*
fifo
=
mps
->
fifo
;
/* clear interrupt and wake up the
work queu
e */
/* clear interrupt and wake up the
rx/tx routin
e */
if
(
in_be32
(
&
fifo
->
txisr
)
&
in_be32
(
&
fifo
->
tximr
)
&
MPC512x_PSC_FIFO_EMPTY
)
{
out_be32
(
&
fifo
->
txisr
,
MPC512x_PSC_FIFO_EMPTY
);
out_be32
(
&
fifo
->
tximr
,
0
);
complete
(
&
mps
->
done
);
complete
(
&
mps
->
txisr
done
);
return
IRQ_HANDLED
;
}
return
IRQ_NONE
;
...
...
@@ -444,18 +490,18 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
if
(
pdata
==
NULL
)
{
mps
->
cs_control
=
mpc512x_spi_cs_control
;
mps
->
sysclk
=
0
;
master
->
bus_num
=
bus_num
;
}
else
{
mps
->
cs_control
=
pdata
->
cs_control
;
mps
->
sysclk
=
pdata
->
sysclk
;
master
->
bus_num
=
pdata
->
bus_num
;
master
->
num_chipselect
=
pdata
->
max_chipselect
;
}
master
->
mode_bits
=
SPI_CPOL
|
SPI_CPHA
|
SPI_CS_HIGH
|
SPI_LSB_FIRST
;
master
->
setup
=
mpc512x_psc_spi_setup
;
master
->
transfer
=
mpc512x_psc_spi_transfer
;
master
->
prepare_transfer_hardware
=
mpc512x_psc_spi_prep_xfer_hw
;
master
->
transfer_one_message
=
mpc512x_psc_spi_msg_xfer
;
master
->
unprepare_transfer_hardware
=
mpc512x_psc_spi_unprep_xfer_hw
;
master
->
cleanup
=
mpc512x_psc_spi_cleanup
;
master
->
dev
.
of_node
=
dev
->
of_node
;
...
...
@@ -473,31 +519,18 @@ static int mpc512x_psc_spi_do_probe(struct device *dev, u32 regaddr,
"mpc512x-psc-spi"
,
mps
);
if
(
ret
)
goto
free_master
;
init_completion
(
&
mps
->
txisrdone
);
ret
=
mpc512x_psc_spi_port_config
(
master
,
mps
);
if
(
ret
<
0
)
goto
free_irq
;
spin_lock_init
(
&
mps
->
lock
);
init_completion
(
&
mps
->
done
);
INIT_WORK
(
&
mps
->
work
,
mpc512x_psc_spi_work
);
INIT_LIST_HEAD
(
&
mps
->
queue
);
mps
->
workqueue
=
create_singlethread_workqueue
(
dev_name
(
master
->
dev
.
parent
));
if
(
mps
->
workqueue
==
NULL
)
{
ret
=
-
EBUSY
;
goto
free_irq
;
}
ret
=
spi_register_master
(
master
);
if
(
ret
<
0
)
goto
unreg_master
;
goto
free_irq
;
return
ret
;
unreg_master:
destroy_workqueue
(
mps
->
workqueue
);
free_irq:
free_irq
(
mps
->
irq
,
mps
);
free_master:
...
...
@@ -513,8 +546,6 @@ static int mpc512x_psc_spi_do_remove(struct device *dev)
struct
spi_master
*
master
=
spi_master_get
(
dev_get_drvdata
(
dev
));
struct
mpc512x_psc_spi
*
mps
=
spi_master_get_devdata
(
master
);
flush_workqueue
(
mps
->
workqueue
);
destroy_workqueue
(
mps
->
workqueue
);
spi_unregister_master
(
master
);
free_irq
(
mps
->
irq
,
mps
);
if
(
mps
->
psc
)
...
...
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