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
Kirill Smelkov
linux
Commits
f1632c32
Commit
f1632c32
authored
Sep 01, 2013
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'spi/topic/blackfin-v3' into spi-next
parents
b29bc3df
d9740f6a
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
980 additions
and
1 deletion
+980
-1
drivers/spi/Kconfig
drivers/spi/Kconfig
+8
-1
drivers/spi/Makefile
drivers/spi/Makefile
+1
-0
drivers/spi/spi-bfin-v3.c
drivers/spi/spi-bfin-v3.c
+971
-0
No files found.
drivers/spi/Kconfig
View file @
f1632c32
...
...
@@ -88,10 +88,17 @@ config SPI_BCM2835
config SPI_BFIN5XX
tristate "SPI controller driver for ADI Blackfin5xx"
depends on BLACKFIN
depends on BLACKFIN
&& !BF60x
help
This is the SPI controller master driver for Blackfin 5xx processor.
config SPI_BFIN_V3
tristate "SPI controller v3 for Blackfin"
depends on BF60x
help
This is the SPI controller v3 master driver
found on Blackfin 60x processor.
config SPI_BFIN_SPORT
tristate "SPI bus via Blackfin SPORT"
depends on BLACKFIN
...
...
drivers/spi/Makefile
View file @
f1632c32
...
...
@@ -17,6 +17,7 @@ obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
obj-$(CONFIG_SPI_BCM2835)
+=
spi-bcm2835.o
obj-$(CONFIG_SPI_BCM63XX)
+=
spi-bcm63xx.o
obj-$(CONFIG_SPI_BFIN5XX)
+=
spi-bfin5xx.o
obj-$(CONFIG_SPI_BFIN_V3)
+=
spi-bfin-v3.o
obj-$(CONFIG_SPI_BFIN_SPORT)
+=
spi-bfin-sport.o
obj-$(CONFIG_SPI_BITBANG)
+=
spi-bitbang.o
obj-$(CONFIG_SPI_BUTTERFLY)
+=
spi-butterfly.o
...
...
drivers/spi/spi-bfin-v3.c
0 → 100644
View file @
f1632c32
/*
* Analog Devices SPI3 controller driver
*
* Copyright (c) 2013 Analog Devices Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <asm/bfin_spi3.h>
#include <asm/cacheflush.h>
#include <asm/dma.h>
#include <asm/portmux.h>
enum
bfin_spi_state
{
START_STATE
,
RUNNING_STATE
,
DONE_STATE
,
ERROR_STATE
};
struct
bfin_spi_master
;
struct
bfin_spi_transfer_ops
{
void
(
*
write
)
(
struct
bfin_spi_master
*
);
void
(
*
read
)
(
struct
bfin_spi_master
*
);
void
(
*
duplex
)
(
struct
bfin_spi_master
*
);
};
/* runtime info for spi master */
struct
bfin_spi_master
{
/* SPI framework hookup */
struct
spi_master
*
master
;
/* Regs base of SPI controller */
struct
bfin_spi_regs
__iomem
*
regs
;
/* Pin request list */
u16
*
pin_req
;
/* Message Transfer pump */
struct
tasklet_struct
pump_transfers
;
/* Current message transfer state info */
struct
spi_message
*
cur_msg
;
struct
spi_transfer
*
cur_transfer
;
struct
bfin_spi_device
*
cur_chip
;
unsigned
transfer_len
;
/* transfer buffer */
void
*
tx
;
void
*
tx_end
;
void
*
rx
;
void
*
rx_end
;
/* dma info */
unsigned
int
tx_dma
;
unsigned
int
rx_dma
;
dma_addr_t
tx_dma_addr
;
dma_addr_t
rx_dma_addr
;
unsigned
long
dummy_buffer
;
/* used in unidirectional transfer */
unsigned
long
tx_dma_size
;
unsigned
long
rx_dma_size
;
int
tx_num
;
int
rx_num
;
/* store register value for suspend/resume */
u32
control
;
u32
ssel
;
unsigned
long
sclk
;
enum
bfin_spi_state
state
;
const
struct
bfin_spi_transfer_ops
*
ops
;
};
struct
bfin_spi_device
{
u32
control
;
u32
clock
;
u32
ssel
;
u8
cs
;
u16
cs_chg_udelay
;
/* Some devices require > 255usec delay */
u32
cs_gpio
;
u32
tx_dummy_val
;
/* tx value for rx only transfer */
bool
enable_dma
;
const
struct
bfin_spi_transfer_ops
*
ops
;
};
static
void
bfin_spi_enable
(
struct
bfin_spi_master
*
drv_data
)
{
bfin_write_or
(
&
drv_data
->
regs
->
control
,
SPI_CTL_EN
);
}
static
void
bfin_spi_disable
(
struct
bfin_spi_master
*
drv_data
)
{
bfin_write_and
(
&
drv_data
->
regs
->
control
,
~
SPI_CTL_EN
);
}
/* Caculate the SPI_CLOCK register value based on input HZ */
static
u32
hz_to_spi_clock
(
u32
sclk
,
u32
speed_hz
)
{
u32
spi_clock
=
sclk
/
speed_hz
;
if
(
spi_clock
)
spi_clock
--
;
return
spi_clock
;
}
static
int
bfin_spi_flush
(
struct
bfin_spi_master
*
drv_data
)
{
unsigned
long
limit
=
loops_per_jiffy
<<
1
;
/* wait for stop and clear stat */
while
(
!
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_SPIF
)
&&
--
limit
)
cpu_relax
();
bfin_write
(
&
drv_data
->
regs
->
status
,
0xFFFFFFFF
);
return
limit
;
}
/* Chip select operation functions for cs_change flag */
static
void
bfin_spi_cs_active
(
struct
bfin_spi_master
*
drv_data
,
struct
bfin_spi_device
*
chip
)
{
if
(
likely
(
chip
->
cs
<
MAX_CTRL_CS
))
bfin_write_and
(
&
drv_data
->
regs
->
ssel
,
~
chip
->
ssel
);
else
gpio_set_value
(
chip
->
cs_gpio
,
0
);
}
static
void
bfin_spi_cs_deactive
(
struct
bfin_spi_master
*
drv_data
,
struct
bfin_spi_device
*
chip
)
{
if
(
likely
(
chip
->
cs
<
MAX_CTRL_CS
))
bfin_write_or
(
&
drv_data
->
regs
->
ssel
,
chip
->
ssel
);
else
gpio_set_value
(
chip
->
cs_gpio
,
1
);
/* Move delay here for consistency */
if
(
chip
->
cs_chg_udelay
)
udelay
(
chip
->
cs_chg_udelay
);
}
/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */
static
inline
void
bfin_spi_cs_enable
(
struct
bfin_spi_master
*
drv_data
,
struct
bfin_spi_device
*
chip
)
{
if
(
chip
->
cs
<
MAX_CTRL_CS
)
bfin_write_or
(
&
drv_data
->
regs
->
ssel
,
chip
->
ssel
>>
8
);
}
static
inline
void
bfin_spi_cs_disable
(
struct
bfin_spi_master
*
drv_data
,
struct
bfin_spi_device
*
chip
)
{
if
(
chip
->
cs
<
MAX_CTRL_CS
)
bfin_write_and
(
&
drv_data
->
regs
->
ssel
,
~
(
chip
->
ssel
>>
8
));
}
/* stop controller and re-config current chip*/
static
void
bfin_spi_restore_state
(
struct
bfin_spi_master
*
drv_data
)
{
struct
bfin_spi_device
*
chip
=
drv_data
->
cur_chip
;
/* Clear status and disable clock */
bfin_write
(
&
drv_data
->
regs
->
status
,
0xFFFFFFFF
);
bfin_write
(
&
drv_data
->
regs
->
rx_control
,
0x0
);
bfin_write
(
&
drv_data
->
regs
->
tx_control
,
0x0
);
bfin_spi_disable
(
drv_data
);
SSYNC
();
/* Load the registers */
bfin_write
(
&
drv_data
->
regs
->
control
,
chip
->
control
);
bfin_write
(
&
drv_data
->
regs
->
clock
,
chip
->
clock
);
bfin_spi_enable
(
drv_data
);
drv_data
->
tx_num
=
drv_data
->
rx_num
=
0
;
/* we always choose tx transfer initiate */
bfin_write
(
&
drv_data
->
regs
->
rx_control
,
SPI_RXCTL_REN
);
bfin_write
(
&
drv_data
->
regs
->
tx_control
,
SPI_TXCTL_TEN
|
SPI_TXCTL_TTI
);
bfin_spi_cs_active
(
drv_data
,
chip
);
}
/* discard invalid rx data and empty rfifo */
static
inline
void
dummy_read
(
struct
bfin_spi_master
*
drv_data
)
{
while
(
!
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
))
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
}
static
void
bfin_spi_u8_write
(
struct
bfin_spi_master
*
drv_data
)
{
dummy_read
(
drv_data
);
while
(
drv_data
->
tx
<
drv_data
->
tx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
(
*
(
u8
*
)(
drv_data
->
tx
++
)));
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
}
}
static
void
bfin_spi_u8_read
(
struct
bfin_spi_master
*
drv_data
)
{
u32
tx_val
=
drv_data
->
cur_chip
->
tx_dummy_val
;
dummy_read
(
drv_data
);
while
(
drv_data
->
rx
<
drv_data
->
rx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
tx_val
);
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
*
(
u8
*
)(
drv_data
->
rx
++
)
=
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
}
}
static
void
bfin_spi_u8_duplex
(
struct
bfin_spi_master
*
drv_data
)
{
dummy_read
(
drv_data
);
while
(
drv_data
->
rx
<
drv_data
->
rx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
(
*
(
u8
*
)(
drv_data
->
tx
++
)));
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
*
(
u8
*
)(
drv_data
->
rx
++
)
=
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
}
}
static
const
struct
bfin_spi_transfer_ops
bfin_bfin_spi_transfer_ops_u8
=
{
.
write
=
bfin_spi_u8_write
,
.
read
=
bfin_spi_u8_read
,
.
duplex
=
bfin_spi_u8_duplex
,
};
static
void
bfin_spi_u16_write
(
struct
bfin_spi_master
*
drv_data
)
{
dummy_read
(
drv_data
);
while
(
drv_data
->
tx
<
drv_data
->
tx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
(
*
(
u16
*
)
drv_data
->
tx
));
drv_data
->
tx
+=
2
;
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
}
}
static
void
bfin_spi_u16_read
(
struct
bfin_spi_master
*
drv_data
)
{
u32
tx_val
=
drv_data
->
cur_chip
->
tx_dummy_val
;
dummy_read
(
drv_data
);
while
(
drv_data
->
rx
<
drv_data
->
rx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
tx_val
);
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
*
(
u16
*
)
drv_data
->
rx
=
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
drv_data
->
rx
+=
2
;
}
}
static
void
bfin_spi_u16_duplex
(
struct
bfin_spi_master
*
drv_data
)
{
dummy_read
(
drv_data
);
while
(
drv_data
->
rx
<
drv_data
->
rx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
(
*
(
u16
*
)
drv_data
->
tx
));
drv_data
->
tx
+=
2
;
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
*
(
u16
*
)
drv_data
->
rx
=
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
drv_data
->
rx
+=
2
;
}
}
static
const
struct
bfin_spi_transfer_ops
bfin_bfin_spi_transfer_ops_u16
=
{
.
write
=
bfin_spi_u16_write
,
.
read
=
bfin_spi_u16_read
,
.
duplex
=
bfin_spi_u16_duplex
,
};
static
void
bfin_spi_u32_write
(
struct
bfin_spi_master
*
drv_data
)
{
dummy_read
(
drv_data
);
while
(
drv_data
->
tx
<
drv_data
->
tx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
(
*
(
u32
*
)
drv_data
->
tx
));
drv_data
->
tx
+=
4
;
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
}
}
static
void
bfin_spi_u32_read
(
struct
bfin_spi_master
*
drv_data
)
{
u32
tx_val
=
drv_data
->
cur_chip
->
tx_dummy_val
;
dummy_read
(
drv_data
);
while
(
drv_data
->
rx
<
drv_data
->
rx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
tx_val
);
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
*
(
u32
*
)
drv_data
->
rx
=
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
drv_data
->
rx
+=
4
;
}
}
static
void
bfin_spi_u32_duplex
(
struct
bfin_spi_master
*
drv_data
)
{
dummy_read
(
drv_data
);
while
(
drv_data
->
rx
<
drv_data
->
rx_end
)
{
bfin_write
(
&
drv_data
->
regs
->
tfifo
,
(
*
(
u32
*
)
drv_data
->
tx
));
drv_data
->
tx
+=
4
;
while
(
bfin_read
(
&
drv_data
->
regs
->
status
)
&
SPI_STAT_RFE
)
cpu_relax
();
*
(
u32
*
)
drv_data
->
rx
=
bfin_read
(
&
drv_data
->
regs
->
rfifo
);
drv_data
->
rx
+=
4
;
}
}
static
const
struct
bfin_spi_transfer_ops
bfin_bfin_spi_transfer_ops_u32
=
{
.
write
=
bfin_spi_u32_write
,
.
read
=
bfin_spi_u32_read
,
.
duplex
=
bfin_spi_u32_duplex
,
};
/* test if there is more transfer to be done */
static
void
bfin_spi_next_transfer
(
struct
bfin_spi_master
*
drv
)
{
struct
spi_message
*
msg
=
drv
->
cur_msg
;
struct
spi_transfer
*
t
=
drv
->
cur_transfer
;
/* Move to next transfer */
if
(
t
->
transfer_list
.
next
!=
&
msg
->
transfers
)
{
drv
->
cur_transfer
=
list_entry
(
t
->
transfer_list
.
next
,
struct
spi_transfer
,
transfer_list
);
drv
->
state
=
RUNNING_STATE
;
}
else
{
drv
->
state
=
DONE_STATE
;
drv
->
cur_transfer
=
NULL
;
}
}
static
void
bfin_spi_giveback
(
struct
bfin_spi_master
*
drv_data
)
{
struct
bfin_spi_device
*
chip
=
drv_data
->
cur_chip
;
bfin_spi_cs_deactive
(
drv_data
,
chip
);
spi_finalize_current_message
(
drv_data
->
master
);
}
static
int
bfin_spi_setup_transfer
(
struct
bfin_spi_master
*
drv
)
{
struct
spi_transfer
*
t
=
drv
->
cur_transfer
;
u32
cr
,
cr_width
;
if
(
t
->
tx_buf
)
{
drv
->
tx
=
(
void
*
)
t
->
tx_buf
;
drv
->
tx_end
=
drv
->
tx
+
t
->
len
;
}
else
{
drv
->
tx
=
NULL
;
}
if
(
t
->
rx_buf
)
{
drv
->
rx
=
t
->
rx_buf
;
drv
->
rx_end
=
drv
->
rx
+
t
->
len
;
}
else
{
drv
->
rx
=
NULL
;
}
drv
->
transfer_len
=
t
->
len
;
/* bits per word setup */
switch
(
t
->
bits_per_word
)
{
case
8
:
cr_width
=
SPI_CTL_SIZE08
;
drv
->
ops
=
&
bfin_bfin_spi_transfer_ops_u8
;
break
;
case
16
:
cr_width
=
SPI_CTL_SIZE16
;
drv
->
ops
=
&
bfin_bfin_spi_transfer_ops_u16
;
break
;
case
32
:
cr_width
=
SPI_CTL_SIZE32
;
drv
->
ops
=
&
bfin_bfin_spi_transfer_ops_u32
;
break
;
default:
return
-
EINVAL
;
}
cr
=
bfin_read
(
&
drv
->
regs
->
control
)
&
~
SPI_CTL_SIZE
;
cr
|=
cr_width
;
bfin_write
(
&
drv
->
regs
->
control
,
cr
);
/* speed setup */
bfin_write
(
&
drv
->
regs
->
clock
,
hz_to_spi_clock
(
drv
->
sclk
,
t
->
speed_hz
));
return
0
;
}
static
int
bfin_spi_dma_xfer
(
struct
bfin_spi_master
*
drv_data
)
{
struct
spi_transfer
*
t
=
drv_data
->
cur_transfer
;
struct
spi_message
*
msg
=
drv_data
->
cur_msg
;
struct
bfin_spi_device
*
chip
=
drv_data
->
cur_chip
;
u32
dma_config
;
unsigned
long
word_count
,
word_size
;
void
*
tx_buf
,
*
rx_buf
;
switch
(
t
->
bits_per_word
)
{
case
8
:
dma_config
=
WDSIZE_8
|
PSIZE_8
;
word_count
=
drv_data
->
transfer_len
;
word_size
=
1
;
break
;
case
16
:
dma_config
=
WDSIZE_16
|
PSIZE_16
;
word_count
=
drv_data
->
transfer_len
/
2
;
word_size
=
2
;
break
;
default:
dma_config
=
WDSIZE_32
|
PSIZE_32
;
word_count
=
drv_data
->
transfer_len
/
4
;
word_size
=
4
;
break
;
}
if
(
!
drv_data
->
rx
)
{
tx_buf
=
drv_data
->
tx
;
rx_buf
=
&
drv_data
->
dummy_buffer
;
drv_data
->
tx_dma_size
=
drv_data
->
transfer_len
;
drv_data
->
rx_dma_size
=
sizeof
(
drv_data
->
dummy_buffer
);
set_dma_x_modify
(
drv_data
->
tx_dma
,
word_size
);
set_dma_x_modify
(
drv_data
->
rx_dma
,
0
);
}
else
if
(
!
drv_data
->
tx
)
{
drv_data
->
dummy_buffer
=
chip
->
tx_dummy_val
;
tx_buf
=
&
drv_data
->
dummy_buffer
;
rx_buf
=
drv_data
->
rx
;
drv_data
->
tx_dma_size
=
sizeof
(
drv_data
->
dummy_buffer
);
drv_data
->
rx_dma_size
=
drv_data
->
transfer_len
;
set_dma_x_modify
(
drv_data
->
tx_dma
,
0
);
set_dma_x_modify
(
drv_data
->
rx_dma
,
word_size
);
}
else
{
tx_buf
=
drv_data
->
tx
;
rx_buf
=
drv_data
->
rx
;
drv_data
->
tx_dma_size
=
drv_data
->
rx_dma_size
=
drv_data
->
transfer_len
;
set_dma_x_modify
(
drv_data
->
tx_dma
,
word_size
);
set_dma_x_modify
(
drv_data
->
rx_dma
,
word_size
);
}
drv_data
->
tx_dma_addr
=
dma_map_single
(
&
msg
->
spi
->
dev
,
(
void
*
)
tx_buf
,
drv_data
->
tx_dma_size
,
DMA_TO_DEVICE
);
if
(
dma_mapping_error
(
&
msg
->
spi
->
dev
,
drv_data
->
tx_dma_addr
))
return
-
ENOMEM
;
drv_data
->
rx_dma_addr
=
dma_map_single
(
&
msg
->
spi
->
dev
,
(
void
*
)
rx_buf
,
drv_data
->
rx_dma_size
,
DMA_FROM_DEVICE
);
if
(
dma_mapping_error
(
&
msg
->
spi
->
dev
,
drv_data
->
rx_dma_addr
))
{
dma_unmap_single
(
&
msg
->
spi
->
dev
,
drv_data
->
tx_dma_addr
,
drv_data
->
tx_dma_size
,
DMA_TO_DEVICE
);
return
-
ENOMEM
;
}
dummy_read
(
drv_data
);
set_dma_x_count
(
drv_data
->
tx_dma
,
word_count
);
set_dma_x_count
(
drv_data
->
rx_dma
,
word_count
);
set_dma_start_addr
(
drv_data
->
tx_dma
,
drv_data
->
tx_dma_addr
);
set_dma_start_addr
(
drv_data
->
rx_dma
,
drv_data
->
rx_dma_addr
);
dma_config
|=
DMAFLOW_STOP
|
RESTART
|
DI_EN
;
set_dma_config
(
drv_data
->
tx_dma
,
dma_config
);
set_dma_config
(
drv_data
->
rx_dma
,
dma_config
|
WNR
);
enable_dma
(
drv_data
->
tx_dma
);
enable_dma
(
drv_data
->
rx_dma
);
SSYNC
();
bfin_write
(
&
drv_data
->
regs
->
rx_control
,
SPI_RXCTL_REN
|
SPI_RXCTL_RDR_NE
);
SSYNC
();
bfin_write
(
&
drv_data
->
regs
->
tx_control
,
SPI_TXCTL_TEN
|
SPI_TXCTL_TTI
|
SPI_TXCTL_TDR_NF
);
return
0
;
}
static
int
bfin_spi_pio_xfer
(
struct
bfin_spi_master
*
drv_data
)
{
struct
spi_message
*
msg
=
drv_data
->
cur_msg
;
if
(
!
drv_data
->
rx
)
{
/* write only half duplex */
drv_data
->
ops
->
write
(
drv_data
);
if
(
drv_data
->
tx
!=
drv_data
->
tx_end
)
return
-
EIO
;
}
else
if
(
!
drv_data
->
tx
)
{
/* read only half duplex */
drv_data
->
ops
->
read
(
drv_data
);
if
(
drv_data
->
rx
!=
drv_data
->
rx_end
)
return
-
EIO
;
}
else
{
/* full duplex mode */
drv_data
->
ops
->
duplex
(
drv_data
);
if
(
drv_data
->
tx
!=
drv_data
->
tx_end
)
return
-
EIO
;
}
if
(
!
bfin_spi_flush
(
drv_data
))
return
-
EIO
;
msg
->
actual_length
+=
drv_data
->
transfer_len
;
tasklet_schedule
(
&
drv_data
->
pump_transfers
);
return
0
;
}
static
void
bfin_spi_pump_transfers
(
unsigned
long
data
)
{
struct
bfin_spi_master
*
drv_data
=
(
struct
bfin_spi_master
*
)
data
;
struct
spi_message
*
msg
=
NULL
;
struct
spi_transfer
*
t
=
NULL
;
struct
bfin_spi_device
*
chip
=
NULL
;
int
ret
;
/* Get current state information */
msg
=
drv_data
->
cur_msg
;
t
=
drv_data
->
cur_transfer
;
chip
=
drv_data
->
cur_chip
;
/* Handle for abort */
if
(
drv_data
->
state
==
ERROR_STATE
)
{
msg
->
status
=
-
EIO
;
bfin_spi_giveback
(
drv_data
);
return
;
}
if
(
drv_data
->
state
==
RUNNING_STATE
)
{
if
(
t
->
delay_usecs
)
udelay
(
t
->
delay_usecs
);
if
(
t
->
cs_change
)
bfin_spi_cs_deactive
(
drv_data
,
chip
);
bfin_spi_next_transfer
(
drv_data
);
t
=
drv_data
->
cur_transfer
;
}
/* Handle end of message */
if
(
drv_data
->
state
==
DONE_STATE
)
{
msg
->
status
=
0
;
bfin_spi_giveback
(
drv_data
);
return
;
}
if
((
t
->
len
==
0
)
||
(
t
->
tx_buf
==
NULL
&&
t
->
rx_buf
==
NULL
))
{
/* Schedule next transfer tasklet */
tasklet_schedule
(
&
drv_data
->
pump_transfers
);
return
;
}
ret
=
bfin_spi_setup_transfer
(
drv_data
);
if
(
ret
)
{
msg
->
status
=
ret
;
bfin_spi_giveback
(
drv_data
);
}
bfin_write
(
&
drv_data
->
regs
->
status
,
0xFFFFFFFF
);
bfin_spi_cs_active
(
drv_data
,
chip
);
drv_data
->
state
=
RUNNING_STATE
;
if
(
chip
->
enable_dma
)
ret
=
bfin_spi_dma_xfer
(
drv_data
);
else
ret
=
bfin_spi_pio_xfer
(
drv_data
);
if
(
ret
)
{
msg
->
status
=
ret
;
bfin_spi_giveback
(
drv_data
);
}
}
static
int
bfin_spi_transfer_one_message
(
struct
spi_master
*
master
,
struct
spi_message
*
m
)
{
struct
bfin_spi_master
*
drv_data
=
spi_master_get_devdata
(
master
);
drv_data
->
cur_msg
=
m
;
drv_data
->
cur_chip
=
spi_get_ctldata
(
drv_data
->
cur_msg
->
spi
);
bfin_spi_restore_state
(
drv_data
);
drv_data
->
state
=
START_STATE
;
drv_data
->
cur_transfer
=
list_entry
(
drv_data
->
cur_msg
->
transfers
.
next
,
struct
spi_transfer
,
transfer_list
);
tasklet_schedule
(
&
drv_data
->
pump_transfers
);
return
0
;
}
#define MAX_SPI_SSEL 7
static
const
u16
ssel
[][
MAX_SPI_SSEL
]
=
{
{
P_SPI0_SSEL1
,
P_SPI0_SSEL2
,
P_SPI0_SSEL3
,
P_SPI0_SSEL4
,
P_SPI0_SSEL5
,
P_SPI0_SSEL6
,
P_SPI0_SSEL7
},
{
P_SPI1_SSEL1
,
P_SPI1_SSEL2
,
P_SPI1_SSEL3
,
P_SPI1_SSEL4
,
P_SPI1_SSEL5
,
P_SPI1_SSEL6
,
P_SPI1_SSEL7
},
{
P_SPI2_SSEL1
,
P_SPI2_SSEL2
,
P_SPI2_SSEL3
,
P_SPI2_SSEL4
,
P_SPI2_SSEL5
,
P_SPI2_SSEL6
,
P_SPI2_SSEL7
},
};
static
int
bfin_spi_setup
(
struct
spi_device
*
spi
)
{
struct
bfin_spi_master
*
drv_data
=
spi_master_get_devdata
(
spi
->
master
);
struct
bfin_spi_device
*
chip
=
spi_get_ctldata
(
spi
);
u32
bfin_ctl_reg
=
SPI_CTL_ODM
|
SPI_CTL_PSSE
;
int
ret
=
-
EINVAL
;
if
(
!
chip
)
{
struct
bfin_spi3_chip
*
chip_info
=
spi
->
controller_data
;
chip
=
kzalloc
(
sizeof
(
*
chip
),
GFP_KERNEL
);
if
(
!
chip
)
{
dev_err
(
&
spi
->
dev
,
"can not allocate chip data
\n
"
);
return
-
ENOMEM
;
}
if
(
chip_info
)
{
if
(
chip_info
->
control
&
~
bfin_ctl_reg
)
{
dev_err
(
&
spi
->
dev
,
"do not set bits that the SPI framework manages
\n
"
);
goto
error
;
}
chip
->
control
=
chip_info
->
control
;
chip
->
cs_chg_udelay
=
chip_info
->
cs_chg_udelay
;
chip
->
tx_dummy_val
=
chip_info
->
tx_dummy_val
;
chip
->
enable_dma
=
chip_info
->
enable_dma
;
}
chip
->
cs
=
spi
->
chip_select
;
if
(
chip
->
cs
<
MAX_CTRL_CS
)
{
chip
->
ssel
=
(
1
<<
chip
->
cs
)
<<
8
;
ret
=
peripheral_request
(
ssel
[
spi
->
master
->
bus_num
]
[
chip
->
cs
-
1
],
dev_name
(
&
spi
->
dev
));
if
(
ret
)
{
dev_err
(
&
spi
->
dev
,
"peripheral_request() error
\n
"
);
goto
error
;
}
}
else
{
chip
->
cs_gpio
=
chip
->
cs
-
MAX_CTRL_CS
;
ret
=
gpio_request_one
(
chip
->
cs_gpio
,
GPIOF_OUT_INIT_HIGH
,
dev_name
(
&
spi
->
dev
));
if
(
ret
)
{
dev_err
(
&
spi
->
dev
,
"gpio_request_one() error
\n
"
);
goto
error
;
}
}
spi_set_ctldata
(
spi
,
chip
);
}
/* force a default base state */
chip
->
control
&=
bfin_ctl_reg
;
if
(
spi
->
mode
&
SPI_CPOL
)
chip
->
control
|=
SPI_CTL_CPOL
;
if
(
spi
->
mode
&
SPI_CPHA
)
chip
->
control
|=
SPI_CTL_CPHA
;
if
(
spi
->
mode
&
SPI_LSB_FIRST
)
chip
->
control
|=
SPI_CTL_LSBF
;
chip
->
control
|=
SPI_CTL_MSTR
;
/* we choose software to controll cs */
chip
->
control
&=
~
SPI_CTL_ASSEL
;
chip
->
clock
=
hz_to_spi_clock
(
drv_data
->
sclk
,
spi
->
max_speed_hz
);
bfin_spi_cs_enable
(
drv_data
,
chip
);
bfin_spi_cs_deactive
(
drv_data
,
chip
);
return
0
;
error:
if
(
chip
)
{
kfree
(
chip
);
spi_set_ctldata
(
spi
,
NULL
);
}
return
ret
;
}
static
void
bfin_spi_cleanup
(
struct
spi_device
*
spi
)
{
struct
bfin_spi_device
*
chip
=
spi_get_ctldata
(
spi
);
struct
bfin_spi_master
*
drv_data
=
spi_master_get_devdata
(
spi
->
master
);
if
(
!
chip
)
return
;
if
(
chip
->
cs
<
MAX_CTRL_CS
)
{
peripheral_free
(
ssel
[
spi
->
master
->
bus_num
]
[
chip
->
cs
-
1
]);
bfin_spi_cs_disable
(
drv_data
,
chip
);
}
else
{
gpio_free
(
chip
->
cs_gpio
);
}
kfree
(
chip
);
spi_set_ctldata
(
spi
,
NULL
);
}
static
irqreturn_t
bfin_spi_tx_dma_isr
(
int
irq
,
void
*
dev_id
)
{
struct
bfin_spi_master
*
drv_data
=
dev_id
;
u32
dma_stat
=
get_dma_curr_irqstat
(
drv_data
->
tx_dma
);
clear_dma_irqstat
(
drv_data
->
tx_dma
);
if
(
dma_stat
&
DMA_DONE
)
{
drv_data
->
tx_num
++
;
}
else
{
dev_err
(
&
drv_data
->
master
->
dev
,
"spi tx dma error: %d
\n
"
,
dma_stat
);
if
(
drv_data
->
tx
)
drv_data
->
state
=
ERROR_STATE
;
}
bfin_write_and
(
&
drv_data
->
regs
->
tx_control
,
~
SPI_TXCTL_TDR_NF
);
return
IRQ_HANDLED
;
}
static
irqreturn_t
bfin_spi_rx_dma_isr
(
int
irq
,
void
*
dev_id
)
{
struct
bfin_spi_master
*
drv_data
=
dev_id
;
struct
spi_message
*
msg
=
drv_data
->
cur_msg
;
u32
dma_stat
=
get_dma_curr_irqstat
(
drv_data
->
rx_dma
);
clear_dma_irqstat
(
drv_data
->
rx_dma
);
if
(
dma_stat
&
DMA_DONE
)
{
drv_data
->
rx_num
++
;
/* we may fail on tx dma */
if
(
drv_data
->
state
!=
ERROR_STATE
)
msg
->
actual_length
+=
drv_data
->
transfer_len
;
}
else
{
drv_data
->
state
=
ERROR_STATE
;
dev_err
(
&
drv_data
->
master
->
dev
,
"spi rx dma error: %d
\n
"
,
dma_stat
);
}
bfin_write
(
&
drv_data
->
regs
->
tx_control
,
0
);
bfin_write
(
&
drv_data
->
regs
->
rx_control
,
0
);
if
(
drv_data
->
rx_num
!=
drv_data
->
tx_num
)
dev_dbg
(
&
drv_data
->
master
->
dev
,
"dma interrupt missing: tx=%d,rx=%d
\n
"
,
drv_data
->
tx_num
,
drv_data
->
rx_num
);
tasklet_schedule
(
&
drv_data
->
pump_transfers
);
return
IRQ_HANDLED
;
}
static
int
bfin_spi_probe
(
struct
platform_device
*
pdev
)
{
struct
device
*
dev
=
&
pdev
->
dev
;
struct
bfin_spi3_master
*
info
=
dev
->
platform_data
;
struct
spi_master
*
master
;
struct
bfin_spi_master
*
drv_data
;
struct
resource
*
mem
,
*
res
;
unsigned
int
tx_dma
,
rx_dma
;
unsigned
long
sclk
;
int
ret
;
if
(
!
info
)
{
dev_err
(
dev
,
"platform data missing!
\n
"
);
return
-
ENODEV
;
}
sclk
=
get_sclk1
();
if
(
!
sclk
)
{
dev_err
(
dev
,
"can not get sclk1
\n
"
);
return
-
ENXIO
;
}
/* get register base and tx/rx dma */
mem
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
mem
)
{
dev_err
(
dev
,
"can not get register base
\n
"
);
return
-
ENXIO
;
}
res
=
platform_get_resource
(
pdev
,
IORESOURCE_DMA
,
0
);
if
(
!
res
)
{
dev_err
(
dev
,
"can not get tx dma resource
\n
"
);
return
-
ENXIO
;
}
tx_dma
=
res
->
start
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_DMA
,
1
);
if
(
!
res
)
{
dev_err
(
dev
,
"can not get rx dma resource
\n
"
);
return
-
ENXIO
;
}
rx_dma
=
res
->
start
;
/* allocate master with space for drv_data */
master
=
spi_alloc_master
(
dev
,
sizeof
(
*
drv_data
));
if
(
!
master
)
{
dev_err
(
dev
,
"can not alloc spi_master
\n
"
);
return
-
ENOMEM
;
}
platform_set_drvdata
(
pdev
,
master
);
/* the mode bits supported by this driver */
master
->
mode_bits
=
SPI_CPOL
|
SPI_CPHA
|
SPI_LSB_FIRST
;
master
->
bus_num
=
pdev
->
id
;
master
->
num_chipselect
=
info
->
num_chipselect
;
master
->
cleanup
=
bfin_spi_cleanup
;
master
->
setup
=
bfin_spi_setup
;
master
->
transfer_one_message
=
bfin_spi_transfer_one_message
;
master
->
bits_per_word_mask
=
BIT
(
32
-
1
)
|
BIT
(
16
-
1
)
|
BIT
(
8
-
1
);
drv_data
=
spi_master_get_devdata
(
master
);
drv_data
->
master
=
master
;
drv_data
->
tx_dma
=
tx_dma
;
drv_data
->
rx_dma
=
rx_dma
;
drv_data
->
pin_req
=
info
->
pin_req
;
drv_data
->
sclk
=
sclk
;
drv_data
->
regs
=
devm_ioremap_resource
(
dev
,
mem
);
if
(
IS_ERR
(
drv_data
->
regs
))
{
ret
=
PTR_ERR
(
drv_data
->
regs
);
goto
err_put_master
;
}
/* request tx and rx dma */
ret
=
request_dma
(
tx_dma
,
"SPI_TX_DMA"
);
if
(
ret
)
{
dev_err
(
dev
,
"can not request SPI TX DMA channel
\n
"
);
goto
err_put_master
;
}
set_dma_callback
(
tx_dma
,
bfin_spi_tx_dma_isr
,
drv_data
);
ret
=
request_dma
(
rx_dma
,
"SPI_RX_DMA"
);
if
(
ret
)
{
dev_err
(
dev
,
"can not request SPI RX DMA channel
\n
"
);
goto
err_free_tx_dma
;
}
set_dma_callback
(
drv_data
->
rx_dma
,
bfin_spi_rx_dma_isr
,
drv_data
);
/* request CLK, MOSI and MISO */
ret
=
peripheral_request_list
(
drv_data
->
pin_req
,
"bfin-spi3"
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"can not request spi pins
\n
"
);
goto
err_free_rx_dma
;
}
bfin_write
(
&
drv_data
->
regs
->
control
,
SPI_CTL_MSTR
|
SPI_CTL_CPHA
);
bfin_write
(
&
drv_data
->
regs
->
ssel
,
0x0000FE00
);
bfin_write
(
&
drv_data
->
regs
->
delay
,
0x0
);
tasklet_init
(
&
drv_data
->
pump_transfers
,
bfin_spi_pump_transfers
,
(
unsigned
long
)
drv_data
);
/* register with the SPI framework */
ret
=
spi_register_master
(
master
);
if
(
ret
)
{
dev_err
(
dev
,
"can not register spi master
\n
"
);
goto
err_free_peripheral
;
}
return
ret
;
err_free_peripheral:
peripheral_free_list
(
drv_data
->
pin_req
);
err_free_rx_dma:
free_dma
(
rx_dma
);
err_free_tx_dma:
free_dma
(
tx_dma
);
err_put_master:
spi_master_put
(
master
);
return
ret
;
}
static
int
bfin_spi_remove
(
struct
platform_device
*
pdev
)
{
struct
spi_master
*
master
=
platform_get_drvdata
(
pdev
);
struct
bfin_spi_master
*
drv_data
=
spi_master_get_devdata
(
master
);
bfin_spi_disable
(
drv_data
);
peripheral_free_list
(
drv_data
->
pin_req
);
free_dma
(
drv_data
->
rx_dma
);
free_dma
(
drv_data
->
tx_dma
);
spi_unregister_master
(
drv_data
->
master
);
return
0
;
}
#ifdef CONFIG_PM
static
int
bfin_spi_suspend
(
struct
device
*
dev
)
{
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
struct
bfin_spi_master
*
drv_data
=
spi_master_get_devdata
(
master
);
spi_master_suspend
(
master
);
drv_data
->
control
=
bfin_read
(
&
drv_data
->
regs
->
control
);
drv_data
->
ssel
=
bfin_read
(
&
drv_data
->
regs
->
ssel
);
bfin_write
(
&
drv_data
->
regs
->
control
,
SPI_CTL_MSTR
|
SPI_CTL_CPHA
);
bfin_write
(
&
drv_data
->
regs
->
ssel
,
0x0000FE00
);
dma_disable_irq
(
drv_data
->
rx_dma
);
dma_disable_irq
(
drv_data
->
tx_dma
);
return
0
;
}
static
int
bfin_spi_resume
(
struct
device
*
dev
)
{
struct
spi_master
*
master
=
dev_get_drvdata
(
dev
);
struct
bfin_spi_master
*
drv_data
=
spi_master_get_devdata
(
master
);
int
ret
=
0
;
/* bootrom may modify spi and dma status when resume in spi boot mode */
disable_dma
(
drv_data
->
rx_dma
);
dma_enable_irq
(
drv_data
->
rx_dma
);
dma_enable_irq
(
drv_data
->
tx_dma
);
bfin_write
(
&
drv_data
->
regs
->
control
,
drv_data
->
control
);
bfin_write
(
&
drv_data
->
regs
->
ssel
,
drv_data
->
ssel
);
ret
=
spi_master_resume
(
master
);
if
(
ret
)
{
free_dma
(
drv_data
->
rx_dma
);
free_dma
(
drv_data
->
tx_dma
);
}
return
ret
;
}
#endif
static
const
struct
dev_pm_ops
bfin_spi_pm_ops
=
{
SET_SYSTEM_SLEEP_PM_OPS
(
bfin_spi_suspend
,
bfin_spi_resume
)
};
MODULE_ALIAS
(
"platform:bfin-spi3"
);
static
struct
platform_driver
bfin_spi_driver
=
{
.
driver
=
{
.
name
=
"bfin-spi3"
,
.
owner
=
THIS_MODULE
,
.
pm
=
&
bfin_spi_pm_ops
,
},
.
remove
=
bfin_spi_remove
,
};
module_platform_driver_probe
(
bfin_spi_driver
,
bfin_spi_probe
);
MODULE_DESCRIPTION
(
"Analog Devices SPI3 controller driver"
);
MODULE_AUTHOR
(
"Scott Jiang <Scott.Jiang.Linux@gmail.com>"
);
MODULE_LICENSE
(
"GPL v2"
);
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